{"id":20635727,"url":"https://github.com/phil65/yamling","last_synced_at":"2025-04-15T21:25:14.974Z","repository":{"id":260987800,"uuid":"882931700","full_name":"phil65/yamling","owner":"phil65","description":"Enhanced YAML loading and dumping","archived":false,"fork":false,"pushed_at":"2025-03-16T11:03:09.000Z","size":257,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T01:34:11.370Z","etag":null,"topics":["configuration","configuration-files","configuration-management","python","python3","serialization","yaml","yaml-configuration","yaml-parser"],"latest_commit_sha":null,"homepage":"https://phil65.github.io/yamling/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phil65.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"phil65","custom":["https://www.paypal.me/phil65"]}},"created_at":"2024-11-04T04:14:17.000Z","updated_at":"2025-03-16T11:03:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"183dd422-dc47-4f8c-83ff-115cbcf42b11","html_url":"https://github.com/phil65/yamling","commit_stats":null,"previous_names":["phil65/yamling"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phil65%2Fyamling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phil65%2Fyamling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phil65%2Fyamling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phil65%2Fyamling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phil65","download_url":"https://codeload.github.com/phil65/yamling/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249155679,"owners_count":21221647,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","configuration-files","configuration-management","python","python3","serialization","yaml","yaml-configuration","yaml-parser"],"created_at":"2024-11-16T15:06:54.812Z","updated_at":"2025-04-15T21:25:14.967Z","avatar_url":"https://github.com/phil65.png","language":"Python","funding_links":["https://github.com/sponsors/phil65","https://www.paypal.me/phil65"],"categories":["Python"],"sub_categories":[],"readme":"# yamling\n\n[![PyPI License](https://img.shields.io/pypi/l/yamling.svg)](https://pypi.org/project/yamling/)\n[![Package status](https://img.shields.io/pypi/status/yamling.svg)](https://pypi.org/project/yamling/)\n[![Daily downloads](https://img.shields.io/pypi/dd/yamling.svg)](https://pypi.org/project/yamling/)\n[![Weekly downloads](https://img.shields.io/pypi/dw/yamling.svg)](https://pypi.org/project/yamling/)\n[![Monthly downloads](https://img.shields.io/pypi/dm/yamling.svg)](https://pypi.org/project/yamling/)\n[![Distribution format](https://img.shields.io/pypi/format/yamling.svg)](https://pypi.org/project/yamling/)\n[![Wheel availability](https://img.shields.io/pypi/wheel/yamling.svg)](https://pypi.org/project/yamling/)\n[![Python version](https://img.shields.io/pypi/pyversions/yamling.svg)](https://pypi.org/project/yamling/)\n[![Implementation](https://img.shields.io/pypi/implementation/yamling.svg)](https://pypi.org/project/yamling/)\n[![Releases](https://img.shields.io/github/downloads/phil65/yamling/total.svg)](https://github.com/phil65/yamling/releases)\n[![Github Contributors](https://img.shields.io/github/contributors/phil65/yamling)](https://github.com/phil65/yamling/graphs/contributors)\n[![Github Discussions](https://img.shields.io/github/discussions/phil65/yamling)](https://github.com/phil65/yamling/discussions)\n[![Github Forks](https://img.shields.io/github/forks/phil65/yamling)](https://github.com/phil65/yamling/forks)\n[![Github Issues](https://img.shields.io/github/issues/phil65/yamling)](https://github.com/phil65/yamling/issues)\n[![Github Issues](https://img.shields.io/github/issues-pr/phil65/yamling)](https://github.com/phil65/yamling/pulls)\n[![Github Watchers](https://img.shields.io/github/watchers/phil65/yamling)](https://github.com/phil65/yamling/watchers)\n[![Github Stars](https://img.shields.io/github/stars/phil65/yamling)](https://github.com/phil65/yamling/stars)\n[![Github Repository size](https://img.shields.io/github/repo-size/phil65/yamling)](https://github.com/phil65/yamling)\n[![Github last commit](https://img.shields.io/github/last-commit/phil65/yamling)](https://github.com/phil65/yamling/commits)\n[![Github release date](https://img.shields.io/github/release-date/phil65/yamling)](https://github.com/phil65/yamling/releases)\n[![Github language count](https://img.shields.io/github/languages/count/phil65/yamling)](https://github.com/phil65/yamling)\n[![Github commits this week](https://img.shields.io/github/commit-activity/w/phil65/yamling)](https://github.com/phil65/yamling)\n[![Github commits this month](https://img.shields.io/github/commit-activity/m/phil65/yamling)](https://github.com/phil65/yamling)\n[![Github commits this year](https://img.shields.io/github/commit-activity/y/phil65/yamling)](https://github.com/phil65/yamling)\n[![Package status](https://codecov.io/gh/phil65/yamling/branch/main/graph/badge.svg)](https://codecov.io/gh/phil65/yamling/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![PyUp](https://pyup.io/repos/github/phil65/yamling/shield.svg)](https://pyup.io/repos/github/phil65/yamling/)\n\n[Read the documentation!](https://phil65.github.io/yamling/)\n\n\nYamling is a YAML handling library that provides enhanced loading and dumping capabilities for YAML files. It builds upon [PyYAML](https://pyyaml.org/) to offer additional features like environment variable support, file inclusion, and Jinja2 template resolution.\nSpecial mentions also to `pyyaml_env_tag` as well as `pyyaml-include`, this library exposes these YAML extensions with a unified interface.\n\n## Loading YAML\n\n### Basic Loading\n\nTo load YAML content from a string:\n\n```python\nfrom yamling import load_yaml\n\n# Simple YAML loading\ndata = load_yaml(\"\"\"\nname: John\nage: 30\n\"\"\")\n```\n\nTo load from a file:\n\n```python\nfrom yamling import load_yaml_file\n\n# Load from local file\nconfig = load_yaml_file(\"config.yaml\")\n\n# Load from remote file (S3, HTTP, etc.)\nremote_config = load_yaml_file(\"s3://bucket/config.yaml\")\n```\n\n### Safety Modes\n\nYamling supports three safety modes when loading YAML:\n\n```python\n# Safe mode - most restrictive, recommended for untrusted input\ndata = load_yaml(content, mode=\"safe\")\n\n# Full mode - allows some additional types but restricts dangerous ones\ndata = load_yaml(content, mode=\"full\")\n\n# Unsafe mode - allows all YAML features (default)\ndata = load_yaml(content, mode=\"unsafe\")\n```\n\n\u003e **Warning**\n\u003e Always use \"safe\" mode when loading untrusted YAML content to prevent code execution vulnerabilities.\n\n### File Inclusion\n\nYamling supports including other YAML files using the `!include` tag:\n\n```yaml\n# main.yaml\ndatabase:\n  !include db_config.yaml\nlogging:\n  !include logging_config.yaml\n```\n\nWhen loading, specify the base path for includes:\n\n```python\nconfig = load_yaml_file(\"main.yaml\", include_base_path=\"configs/\")\n```\n\n### Environment Variables\n\nUse the `!ENV` tag to reference environment variables:\n\n```yaml\ndatabase:\n  password: !ENV DB_PASSWORD\n  host: !ENV ${DB_HOST:localhost}  # with default value\n```\n\n### Template Resolution\n\nYamling can resolve [Jinja2](https://jinja.palletsprojects.com/) templates in YAML:\n\n```python\nfrom jinja2 import Environment\nimport yamling\n\nenv = Environment()\nyaml_content = \"\"\"\nmessage: \"Hello {{ name }}!\"\n\"\"\"\n\ndata = load_yaml(\n    yaml_content,\n    resolve_strings=True,\n    jinja_env=env\n)\n```\n\n### Inheritance\n\nYAML files can inherit from other files using the `INHERIT` key. You can specify either a single file or a list of files to inherit from:\n\n```yaml\n# Single inheritance\n# prod.yaml\nINHERIT: base.yaml\ndatabase:\n  host: prod.example.com\n\n# Multiple inheritance\n# prod_with_logging.yaml\nINHERIT:\n  - base.yaml\n  - logging.yaml\n  - monitoring.yaml\ndatabase:\n  host: prod.example.com\n```\n\nWhen using multiple inheritance, files are processed in order, with later files taking precedence over earlier ones. The current file's values take precedence over all inherited values.\n\nFor example:\n\n```yaml\n# base.yaml\ndatabase:\n  host: localhost\n  port: 5432\n  timeout: 30\n\n# logging.yaml\ndatabase:\n  timeout: 60\nlogging:\n  level: INFO\n\n# prod.yaml\nINHERIT:\n  - base.yaml\n  - logging.yaml\ndatabase:\n  host: prod.example.com\n```\n\nWhen loading `prod.yaml`, the final configuration will be:\n\n```yaml\ndatabase:\n  host: prod.example.com  # from prod.yaml\n  port: 5432             # from base.yaml\n  timeout: 60            # from logging.yaml\nlogging:\n  level: INFO            # from logging.yaml\n```\n\nLoad with inheritance enabled:\n\n```python\nconfig = load_yaml_file(\"prod.yaml\", resolve_inherit=True)\n```\n\n\n## Dumping YAML\n\nTo serialize Python objects to YAML:\n\n```python\nfrom yamling import dump_yaml\n\ndata = {\n    \"name\": \"John\",\n    \"scores\": [1, 2, 3],\n    \"active\": True\n}\n\nyaml_string = dump_yaml(data)\n```\nDataclasses and Pydantic v2 models can also get dumped.\n\n### Custom Class Mapping\n\nMap custom classes to built-in types for YAML representation:\n\n```python\nfrom collections import OrderedDict\n\ndata = OrderedDict([(\"b\", 2), (\"a\", 1)])\nyaml_string = dump_yaml(data, class_mappings={OrderedDict: dict})\n```\n\n## Custom Loader Configuration\n\nFor advanced use cases, you can create a custom loader:\n\n```python\nfrom yamling import get_loader\nimport yaml\n\n# Create custom loader with specific features\nloader_cls = get_loader(\n    yaml.SafeLoader,\n    include_base_path=\"configs/\",\n    enable_include=True,\n    enable_env=True,\n    resolve_strings=True,\n    jinja_env=jinja_env,\n    type_converters={int: str}\n)\n\n# Use custom loader\ndata = yaml.load(content, Loader=loader_cls)\n```\n\n## Custom Tag Handling\n\nYamling provides a `YAMLParser` class for handling custom YAML tags. This allows you to define how specific tagged values should be processed during YAML loading.\n\n### Basic Tag Registration\n\nYou can register tag handlers using either a decorator or explicit registration:\n\n```python\nfrom yamling import YAMLParser\n\n# Create parser instance\nyaml_parser = YAMLParser()\n\n# register handler\ndef handle_uppercase(data: str) -\u003e str:\n    return data.upper()\n\nyaml_parser.register_handler(\"uppercase\", handle_uppercase)\n```\n\n### Using Custom Tags\n\nOnce registered, you can use the custom tags in your YAML:\n\n```yaml\n# config.yaml\nmessage: !uppercase \"hello world\"\n```\n\nLoad the YAML using the parser:\n\n```python\n# Load from string\ndata = yaml_parser.load_yaml(\"\"\"\nmessage: !uppercase \"hello world\"\n\"\"\")\n\n# Or load from file\ndata = yaml_parser.load_yaml_file(\"config.yaml\")\n\nprint(data[\"message\"])  # \"HELLO WORLD\"\n```\n\n### Class Registration\n\nFor simple cases where you just want to convert tagged dictionary data into class instances,\nyou can use the `register_class` method. The method will automatically use the lowercase class name\nas the tag name (following YAML tag conventions), but you can override this with a custom tag name:\n\n```python\nfrom yamling import YAMLParser\nfrom dataclasses import dataclass\n\n@dataclass\nclass Person:\n    name: str\n    age: int\n\n@dataclass\nclass HomeAddress:\n    street: str\n    city: str\n\nyaml_parser = YAMLParser()\n\n# Register using class name as tag (will use \"person\" as tag)\nyaml_parser.register_class(Person)\n\n# Register with custom tag name instead of \"homeaddress\"\nyaml_parser.register_class(HomeAddress, \"address\")\n\n# Now you can use it in YAML:\ndata = yaml_parser.load_yaml(\"\"\"\nuser: !person\n  name: John Doe\n  age: 30\nhome: !address\n  street: 123 Main St\n  city: New York\n\"\"\")\n\nprint(data[\"user\"])    # Person(name='John Doe', age=30)\nprint(data[\"home\"])    # HomeAddress(street='123 Main St', city='New York')\n```\n\n### Complex Structures\n\nCustom tags can be used in nested structures and lists:\n\n```yaml\nteam:\n  manager: !person\n    name: Alice Smith\n    age: 45\n  members:\n    - !person\n      name: Bob Johnson\n      age: 30\n    - !person\n      name: Carol White\n      age: 28\nmessages:\n  - !uppercase \"welcome\"\n  - !uppercase \"goodbye\"\n```\n\n### Combining with Other Features\n\nThe `YAMLParser` class supports all of Yamling's standard features:\n\n```python\ndata = yaml_parser.load_yaml_file(\n    \"config.yaml\",\n    mode=\"safe\",                    # Safety mode\n    include_base_path=\"configs/\",   # For !include directives\n    resolve_strings=True,           # Enable Jinja2 template resolution\n    resolve_inherit=True,           # Enable inheritance\n    jinja_env=jinja_env           # Custom Jinja2 environment\n)\n```\n\n### Available Tags\n\nYou can list all registered tags:\n\n```python\ntags = yaml_parser.list_tags()\nprint(tags)  # ['!person', '!uppercase']\n```\n\n\n## Universal load / dump interface\n\nYamling provides a universal load function that can handle YAML, JSON, TOML, and INI files.\nApart from yaml, only stdlib modules are used, so no additional dependencies are required.\nHere's a simple example:\n\n```python\nimport yamling\n\n# Load files based on their extension\nconfig = yamling.load_file(\"config.yaml\")    # YAML\nsettings = yamling.load_file(\"settings.json\") # JSON\nparams = yamling.load_file(\"params.toml\")    # TOML\n\n# Or explicitly specify the format\ndata = yamling.load_file(\"config.txt\", mode=\"yaml\")\n\n# Load directly from strings\nyaml_text = \"\"\"\nname: John\nage: 30\n\"\"\"\ndata = yamling.load(yaml_text, mode=\"yaml\")\n# same in other direction\njson_text = yamling.dump(data, mode=\"json\")\nyaml.dump_file(\"config.yaml\", data)\n```\n\n\u003e **Note**\n\u003e If [orjson](https://github.com/ijl/orjson) is installed, the loader will automatically use it for JSON parsing / dumping, offering significantly better performance compared to the standard `json` module.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphil65%2Fyamling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphil65%2Fyamling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphil65%2Fyamling/lists"}