{"id":20225611,"url":"https://github.com/kalaspuff/cure","last_synced_at":"2026-03-11T01:31:17.026Z","repository":{"id":36458957,"uuid":"224758945","full_name":"kalaspuff/cure","owner":"kalaspuff","description":"🎖 Decorator for adding trailing underscores to keys of keyword arguments using keys of reserved keywords or built-ins. May also convert kwargs to snake case from third party frameworks.","archived":false,"fork":false,"pushed_at":"2024-01-22T09:43:01.000Z","size":425,"stargazers_count":3,"open_issues_count":9,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-30T13:42:40.490Z","etag":null,"topics":["builtins","convention","convert-keys","decorator","keyword-argument","kwargs","library","module","python","python-keywords","python3","reserved","single-trailing-underscore","trailing","trailing-underscore","underscores"],"latest_commit_sha":null,"homepage":"","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/kalaspuff.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,"zenodo":null},"funding":{"github":["kalaspuff"]}},"created_at":"2019-11-29T02:04:30.000Z","updated_at":"2023-09-01T11:04:42.000Z","dependencies_parsed_at":"2025-04-10T17:06:08.508Z","dependency_job_id":"4472a27e-b6ec-4529-b585-c64f70496a7c","html_url":"https://github.com/kalaspuff/cure","commit_stats":{"total_commits":162,"total_committers":4,"mean_commits":40.5,"dds":0.3827160493827161,"last_synced_commit":"7154a00e36c95bfe39efce235d0957b35095d5a3"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/kalaspuff/cure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kalaspuff%2Fcure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kalaspuff%2Fcure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kalaspuff%2Fcure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kalaspuff%2Fcure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kalaspuff","download_url":"https://codeload.github.com/kalaspuff/cure/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kalaspuff%2Fcure/sbom","scorecard":{"id":548156,"data":{"date":"2025-08-11","repo":{"name":"github.com/kalaspuff/cure","commit":"6e49d3edb983a852919f7d1278e7ab7fdbe1f2fd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/kalaspuff/cure/pythonpackage.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/kalaspuff/cure/pythonpackage.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pythonpackage.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/kalaspuff/cure/pythonpackage.yml/master?enable=pin","Warn: downloadThenRun not pinned by hash: .github/workflows/pythonpackage.yml:31","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/12 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/pythonpackage.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-48 / GHSA-fj7x-q9j7-g6q6","Warn: Project is vulnerable to: PYSEC-2024-230 / GHSA-248v-346w-9cwc","Warn: Project is vulnerable to: PYSEC-2023-135 / GHSA-xqr8-7jwr-rhp7","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2023-74 / GHSA-j8r2-6x86-q33q","Warn: Project is vulnerable to: GHSA-34jh-p97f-mpxf","Warn: Project is vulnerable to: PYSEC-2023-212 / GHSA-g4mx-q9vg-27p4","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: PYSEC-2023-192 / GHSA-v845-jxx5-vc9f","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 20 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T10:04:22.036Z","repository_id":36458957,"created_at":"2025-08-20T10:04:22.036Z","updated_at":"2025-08-20T10:04:22.036Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30366051,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["builtins","convention","convert-keys","decorator","keyword-argument","kwargs","library","module","python","python-keywords","python3","reserved","single-trailing-underscore","trailing","trailing-underscore","underscores"],"created_at":"2024-11-14T07:13:54.011Z","updated_at":"2026-03-11T01:31:16.995Z","avatar_url":"https://github.com/kalaspuff.png","language":"Python","funding_links":["https://github.com/sponsors/kalaspuff"],"categories":[],"sub_categories":[],"readme":"# `cure`\n![Python package](https://github.com/kalaspuff/cure/workflows/Python%20package/badge.svg)\n[![pypi](https://badge.fury.io/py/cure.svg)](https://pypi.python.org/pypi/cure/)\n[![Made with Python](https://img.shields.io/pypi/pyversions/cure)](https://www.python.org/)\n[![Type hinted - mypy validated](https://img.shields.io/badge/typehinted-yes-teal)](https://github.com/kalaspuff/cure)\n[![MIT License](https://img.shields.io/github/license/kalaspuff/cure.svg)](https://github.com/kalaspuff/cure/blob/master/LICENSE)\n[![Code coverage](https://codecov.io/gh/kalaspuff/cure/branch/master/graph/badge.svg)](https://codecov.io/gh/kalaspuff/cure/tree/master/cure)\n\n*Library for adding trailing underscores to passed down keyword arguments from third party libraries. Adds the preferred trailing underscore to the key in the kwarg if the key would conflict with the Python reserved keywords or Python built-ins. Methods can be decorated with the `@cure` decorator.*\n\nCan also be used to convert input keyword arguments to snake case. All in all a decorator to put on your functions which take input from third party libraries which are dependant on user input. In my experience this may happen when working with web frameworks that may apply query values as kwargs or when interfacing with GraphQL libraries that will send user input arguments as kwargs.\n\nAs described in *PEP 8 -- Style Guide for Python Code* (https://www.python.org/dev/peps/pep-0008/):\n\u003e The following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):\n\u003e\n\u003e `single_trailing_underscore_`: used by convention to avoid conflicts with Python keyword.\n\n\n## Installation with `pip`\nLike you would install any other Python package, use `pip`, `poetry`, `pipenv` or your weapon of choice.\n```\n$ pip install cure\n```\n\n\n## Usage and examples\n\n#### Use the `@cure.decorator`\n\n```python\nimport cure\n\n@cure.decorator(cure.KEYWORD_TRAILING_UNDERSCORES)\ndef my_function(id_=None, username=None, type_=None):\n    pass\n```\n\nThis function could then be called with the keyword arguments `id`, `username` and/or `type`. Since `id` and `type` are either reserved Python keywords or are Python built-ins and shouldn't be used as names, the decorator will add a trailing underscore to the kwargs before passing them into our function `my_function`.\n\n#### Convert kwargs to snake case as well\n\n```python\nimport cure\n\n@cure.decorator(cure.KEYWORD_TRAILING_UNDERSCORES | cure.KEYWORD_SNAKE_CASE_RECURSIVE)\ndef graphql_resolver(obj, info, resource_id, type_=None):\n    pass\n```\n\nThis function uses both the options of adding trailing underscores to reserved keywords and built-ins as well as the option of converting input kwargs to snake case. Let's say we're dealing with a GraphQL framework which would otherwise call our function like this, depending on the user input `my_function(None, info, resourceId=kwargs[\"resourceId\"], type=kwargs[\"type\"])` or even more likely like `my_function(None, info, **kwargs)` where `kwargs` would be a dict holding the keys `resourceId` and `type`. Since we want our code to be Pythonic and adhere to proper naming conventions the `@cure.decorator` can help out with removing the hurdle of converting the kwargs ourself or by jumping through hoops otherwise required.\n\n#### Available options\n* `cure.KEYWORD_TRAILING_UNDERSCORES`: Adds trailing underscores to keys in keyword arguments that are using a name that is either a reserved keyword or a Python built-in.\n* `cure.KEYWORD_SNAKE_CASE`: Converts keys in keyword arguments to snake case.\n* `cure.KEYWORD_SNAKE_CASE_RECURSIVE`: Converts keys in keyword arguments to snake case. If the keyword argument's value is a dict or a list of dicts it will also convert keys within these to snake case.\n* `cure.KEYWORD_CAMEL_CASE`: Converts keys in keyword arguments to camel case. This is not recommended, but may be used as a reversal of values converted by the snake case decorator.\n* `cure.KEYWORD_CAMEL_CASE_RECURSIVE`: Recursive conversion to camel case.\n\n#### Other functions\nThe following functions are also available from the module.\n\n##### `cure.is_keyword(kw)`\n```python\nimport cure\n\ncure.is_keyword(\"id\")\n# True\ncure.is_keyword(\"type\")\n# True\ncure.is_keyword(\"api\")\n# False\n```\n\n##### `cure.trail_name(kw)`\n```python\nimport cure\n\ncure.trail_name(\"id\")\n# \"id_\"\ncure.trail_name(\"type\")\n# \"type_\"\ncure.trail_name(\"api\")\n# \"api\"\n```\n\n##### `cure.snake_case_name(kw)` and `cure.snake_case_dict(input_dict, recursive)`\n```python\nimport cure\n\ncure.snake_case_name(\"apiSecret\")\n# \"api_secret\"\ncure.snake_case_dict({\"user\": {\"userId\": 4711, \"userLevel\": \"ADMIN\"}}, recursive=True)\n# {'user': {'user_id': 4711, 'user_level': 'ADMIN'}}\n```\n\n##### `cure.camel_case_name(kw)` and `cure.camel_case_dict(input_dict, recursive)`\n```python\nimport cure\n\ncure.camel_case_name(\"api_secret\")\n# \"apiSecret\"\ncure.camel_case_dict({\"user\": {\"user_id\": 4711, \"user_level\": \"ADMIN\"}}, recursive=True)\n# {'user': {'userId': 4711, 'userLevel': 'ADMIN'}}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkalaspuff%2Fcure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkalaspuff%2Fcure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkalaspuff%2Fcure/lists"}