{ "info": { "author": "Sirio Balmelli", "author_email": "sirio.bm@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# Replacement [![Build Status](https://travis-ci.org/siriobalmelli/replacement.svg?branch=master)](https://travis-ci.org/siriobalmelli/replacement)\n\nReplacement is a python utility that parses a yaml template and outputs text.\n\n## Installing\n\n`pip3 install replacement`\n\nor, if you use [nix](https://nixos.org/):\n\n`nix-env --install -A 'nixpkgs.python36Packages.replacement'`\n\n## Introduction\n\nNOTE: the examples given here are further documented in the\n [tests](replacement/tests/README.md).\n\nA `template` is a YAML file containing a `replacement` object.\n\nA replacement object contains a list of `blocks`.\n\n### 1. basic template\n\nA template:\n\n```yaml\n---\n# simplistic template example\n# tests/hello.yaml\nreplacement:\n - text: text\n input:\n hello world\n...\n```\n\nExecute a template using `replacement`:\n\n```bash\n$ replacement -t tests/hello.yaml\nhello world\n```\n\nBlocks always begin with a `directive`, which specifies:\n - the data type the block will `yield`, such as `text` or a `dict`\n - the input data type, such as `file` or `yaml`\n\nIn the block above, `text: text` is the directive;\n it specifies the block takes `text` input and yields `text` output.\n\n### 2. reading from a file\n\nFile `hello.out`:\n\n```\nhello world\n```\n\n```yaml\n---\n# read from file\n# tests/file.yaml\nreplacement:\n - text: file\n input:\n hello.out\n...\n```\n\n```bash\n$ replacement -t tests/file.yaml\nhello world\n```\n\nNotice that:\n\n- The directive in the above block is `text: file`;\n this means: \"yield text, input from a file\".\n- The `input` key is used for the filename to be read;\n in the first example `input` was used to store the literal text input.\n- File paths are relative to the path of the template.\n\n### intermission: schema\n\nA schema of all the replacement directives is contained in [schema.yaml](./schema.yaml).\n\nHere is a snippet showing just the `directive` and `input` portions of a block:\n\n```yaml\n---\n##\n# format of a block\n##\nblock:\n - yield: inputType # this is a directive\n input: inputVal\n\n##\n# definition of schema elements\n##\nschema:\n # 'yield' : what to output (how to format output)\n yield:\n - text # text: a list of newline-separated strings\n - dict # a dictionary of key-value pairs\n - meta # output nothing: set a metadata (substitution dictionary) variable\n\n # 'input' : where to source data\n inputType:\n # we can input everything we output (see 'yield' above)\n - text\n - dict\n - meta # a key-value pair retrieved from substitutions dictionary\n # we can also input from other sources\n - file # open a file on disk\n - eval # python3 eval() statement\n - func # import and then call call a function\n - exec # subprocess execution (usually bash)\n\n inputVal: input_specific\n...\n```\n\n### 3. metadata and substitution\n\nThe `meta` directive specifies that the output of a block should be inserted\n into the \"substitutions dictionary\" (aka: `meta`).\n\n- this implies input must be a valid dictionary\n- replacement will parse YAML or JSON (which is a subset of YAML)\n\nThe `proc` keyword specifies that block output should be processed\n (string substitution).\nValid `proc` directives are:\n\n- `format` :: `str.format()`\n- `substitute` :: `string.Template().substitute()`\n- `safe_substitute` :: `string.Template().safe_substitute()`\n\n```yaml\n---\n# metadata\n# tests/metadata.yaml\nreplacement:\n # parse 'input' and insert the resulting dictionary into 'meta'\n - meta: dict\n # metadata may be given as an object\n input:\n version: 1.1\n tag: my_awesome_tag\n - meta: text\n # metadata may be given as text which can be parsed as valid YAML\n input: |\n ---\n message: hello world\n ...\n - meta: text\n # metadata may also be given as JSON\n input: |\n { \"hi\": 5 }\n # use 'proc' to specify that 'str.format(**meta)' should be run on output\n - text: text\n proc: format\n input: |\n v{version} tag \"{tag}\"\n - text: text\n proc: substitute\n input: |\n message $message\n - text: text\n proc: safe_substitute\n input: |\n hi $hi\n this value may not exist - $nonexistent\n...\n```\n\n```bash\n$ replacement -t tests/metadata.yaml\nv1.1 tag \"my_awesome_tag\"\nmessage hello world\nhi 5\nthis value may not exist - $nonexistent\n```\n\nMetadata can also be read from a file.\n\nFile `a.json`:\n\n```json\n{\n\"hello\": \"world\",\n\"hi\": 5,\n\"list\": [ 1, 2, 3, 4 ]\n}\n```\n\n```yaml\n---\n# metadata\n# tests/meta_file.yaml\nreplacement:\n # parse file, inserting result into metadata dictionary\n - meta: file\n input:\n a.json\n # string substitution with 'proc'\n - text: text\n proc: format\n input: |\n hello {hello}\n hi {hi}\n list {list}\n...\n```\n\n```bash\n$ replacement -t tests/meta_file.yaml\nhello world\nhi 5\nlist [1, 2, 3, 4]\n```\n\n### 3a. value -> dictionary using `key`\n\n```yaml\n---\n# insert contents of file as metadata\n# tests/file_as_meta.yaml\nreplacement:\n - text: dict\n input:\n # produces {\"hello\": \"hi\"}\n - dict: text\n key: hello\n input: |\n hi\n - dict: file\n key: contents\n input: hello.out\n\n - meta: file\n key: data\n input: prep.out\n - dict: dict\n prep: substitute\n input:\n also: $data\n\n - meta: file\n key: largefile\n input: recurse.out\n\n - dict: dict\n prep: format\n input:\n hello: world\n hi: |\n {largefile}\n...\n```\n\n```bash\n---\nhello: hi\ncontents: hello world\nalso: hello world\nhi: |-\n recursed inline\n v1.1 tag \"my_awesome_tag\"\n message hello world\n hi 5\n this value may not exist - I exist I promise!\n...\n```\n\n### 4. nesting\n\n1. Blocks are executed in sequence, and may nest.\n - nested blocks may extend/alter `meta`, which will be seen by\n later blocks or child blocks, but **not** parent blocks.\n\n```yaml\n---\n# nesting and dictionaries\n# tests/nesting.yaml\nreplacement:\n # parse 'input' and insert the resulting dictionary into 'meta'\n - meta: dict\n input:\n version: 1.1\n # same thing, but parse *text* input, instead of a dictionary\n - meta: text\n input: | # note the '|'\n ---\n tag: my_awesome_tag\n ...\n # use 'proc' to specify that 'str.format(**meta)' should be run on output\n - text: text\n proc: format\n input: |\n v{version} tag \"{tag}\"\n - text: text\n input:\n # metadata additions/changes seen by later blocks and any children,\n # but seen outside this list\n - meta: dict\n input:\n version: 1.0\n - text: text\n proc: format\n input: |\n #v{version} tag \"{tag}\" (version clobbered in inner scope)\n - text: file\n input:\n hello.out # contains 'hello world'\n - text: text\n proc: format\n input: |\n outer still v{version}\n...\n```\n\n```bash\n$ replacement -t tests/nesting.yaml\nv1.1 tag \"my_awesome_tag\"\n#v1.0 tag \"my_awesome_tag\" (version clobbered in inner scope)\nhello world\nouter still v1.1\n```\n\n### 5. preprocessing\n\nSubstitution can also be performed on the values of the block itself\n*before* it is parsed.\nThe keyword `prep` is used, with the same semantics as `proc` (above):\n\n```yaml\n---\n# preprocessing\n# tests/prep.yaml\nreplacement:\n - meta: dict\n input:\n filename: hello.out\n # preprocessing will substitute {filename} before evaluating 'file' input\n - text: file\n prep: format\n input: |\n {filename}\n...\n```\n\n```bash\n$ replacement -t tests/prep.yaml\nhello world\n```\n\n### 6. access to python `eval`\n\n```yaml\n---\n# use of eval\n# tests/an_eval.yaml\nreplacement:\n # 'eval' returning a dictionary that can me appended to 'meta'\n - meta: eval\n input: |\n {\"hello\": 5 + 1}\n - text: text\n prep: format\n input: |\n hello {hello}\n # eval returning a scalar value\n - text: eval\n prep: format\n input: |\n {hello}**3\n...\n```\n\n```bash\n$ replacement -t tests/an_eval.yaml\nhello 6\n216\n```\n\n### 7. python `exec`\n\nSometimes it is advantageous to manipulate the runtime environment.\n\nIn the below example, this is being used to import a module which will\nbe needed by a subsequency call to `eval`.\n\n```yaml\n---\n# use of eval with an exec statement to execute an import call\n# tests/eval_exec.yaml\nreplacement:\n - meta: exec\n input: |\n global IPv4Network\n from ipaddress import IPv4Network\n - meta: eval\n input: |\n {'gateway': str([h for h in IPv4Network('192.168.1.0/24').hosts()][-1])}\n - text: text\n prep: format\n input: |\n my gateway is {gateway}\n...\n```\n\n```bash\n$ replacement -t tests/eval_exec.yaml\nmy gateway is 192.168.1.254\n```\n\nNOTE: python `exec` is very powerful,\nplease *avoid* running `replacement` as a privileged user.\n\n### 8. imports and function execution\n\nThe `func` input directive can be used to find and call an external function\n(see [demo.py](tests/demo.py) for reference):\n\n```yaml\n---\n# function execution\n# tests/func.yaml\nreplacement:\n # run a function returning a dictionary, and merge that into 'meta'\n - meta: func\n args:\n existing: {'original': 'thesis'}\n # NOTE that this is sensitive to PYTHONPATH\n input: |\n ret_a_dict ./demo.py\n - text: text\n prep: format\n input: |\n original {original}\n secret {secret}\n\n # run a function returning a dictionary and export return as JSON\n - text: func\n options:\n - json # emit JSON rather than YAML (the default)\n args:\n existing: {'original': 'thesis'}\n input: |\n ret_a_dict ./demo.py\n\n # run a function returning an IOStream\n - text: func\n args: {}\n # notice non-portable BRAINDEAD python dot.notation\n # ... will break if 'replacement' (the script) is MOVED vis-a-vis demo.py\n # (of OF COURSE the CWD is blithely ignored ... why would ANYONE use it LOL)\n # TLDR: use the '[symbol] [path]' notation in the preceding example\n input: |\n tests.demo.ret_a_stream\n\n # A function returning a list of lines of text\n # (using the proper, cleaner import strategy)\n - text: func\n args:\n an_arg: [\"question\"]\n input: |\n ret_a_list ./demo.py\n\n # a static function inside a class\n - text: func\n input: |\n aClass.invented_list ./demo.py\n```\n\n```bash\n$ replacement -t tests/func.yaml\noriginal thesis\nsecret 42\n{\"secret\": 42, \"original\": \"thesis\"}\n1. hello\n2. world\n42\nmeaning\nquestion\nhello\nfrom\nstaticmethod\n```\n\n## 9. Recursion\n\nOr, how I learned to stop worrying and have templates import other templates.\n\n```yaml\n---\n# recursion (nested 'replacement' templates)\n# tests/recurse.yaml\nreplacement:\n # parse a 'replacement' template inline\n - replacement: text\n input: |\n ---\n replacement:\n - text: text\n input: |\n recursed inline\n ...\n - meta: dict\n input:\n nonexistent: \"I exist I promise!\"\n # parse a replacement template from a file\n # NOTE *relative* path)\n # NOTE our 'meta' dictionary is propagated to child replacement being parsed\n - replacement: file\n input: metadata.yaml\n...\n```\n\n```bash\n$ replacement -t tests/recurse.yaml\nrecursed inline\nv1.1 tag \"my_awesome_tag\"\nmessage hello world\nhi 5\nthis value may not exist - I exist I promise!\n```\n\n## NOTES\n\n1. This documentation is itself built as a template, so that the content\nof `.yaml` tests and `.out` results is kept in sync with the [tests](./tests)\ndirectory.\nSee [README.template.yaml](./README.template.yaml) and [gen_readme.sh](./gen_readme.sh).\n\n## Project TODO\n\nProject TODO list\n\n1. subprocess execution and output capture\n\n1. accept template from STDIN, not just a template file\n\n1. Packaging:\n - proper test runner (perhaps [tox](https://tox.readthedocs.io/en/latest/)?)\n - add test runner to all the builds\n\n1. Dependency output command: runs all preprocessing and outputs a list of\n file dependencies.\nFor use e.g. in Makefiles.\n\n1. Express [the schema](replacement/schema.yaml) formally and write validation code for it.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/siriobalmelli/replacement", "keywords": "replacement template substitute compile", "license": "Apache", "maintainer": "", "maintainer_email": "", "name": "replacement", "package_url": "https://pypi.org/project/replacement/", "platform": "", "project_url": "https://pypi.org/project/replacement/", "project_urls": { "Homepage": "https://github.com/siriobalmelli/replacement" }, "release_url": "https://pypi.org/project/replacement/0.3.6/", "requires_dist": [ "ruamel.yaml" ], "requires_python": ">=3.5", "summary": "Replacement is a python utility that parses a yaml template and outputs text.", "version": "0.3.6" }, "last_serial": 4966397, "releases": { "0.2.6": [ { "comment_text": "", "digests": { "md5": "3a98257894dd7d1c796b40235f3909d8", "sha256": "e9dc14820cf3cebd8fafd2dcc3233a4677bb47d00825ed767aeb2d5d26a518ca" }, "downloads": -1, "filename": "replacement-0.2.6-py3-none-any.whl", "has_sig": false, "md5_digest": "3a98257894dd7d1c796b40235f3909d8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 13483, "upload_time": "2018-10-28T11:13:38", "url": "https://files.pythonhosted.org/packages/6b/87/18cf772773121252277857a4960d69fcb56bbab4cd0da05eee3aa2667f7d/replacement-0.2.6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "52dca97b0355fde47b903d0e48b46d38", "sha256": "57409f52315f915cf1de1ef5af6ae66641e2344be83b0e897a82c7b52280a502" }, "downloads": -1, "filename": "replacement-0.2.6.tar.gz", "has_sig": false, "md5_digest": "52dca97b0355fde47b903d0e48b46d38", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 12496, "upload_time": "2018-10-28T11:13:40", "url": "https://files.pythonhosted.org/packages/cd/ed/23fb26b7975727600c9c36bd9ce7a684ae809cafe7f0c8e859ba827765ae/replacement-0.2.6.tar.gz" } ], "0.2.8": [ { "comment_text": "", "digests": { "md5": "1595ddc259b75cf2a2eefc2759193b03", "sha256": "846bbbe230c0a4ef0144c904d6c172fe5b3146497a5ac23b7a792652b8e07900" }, "downloads": -1, "filename": "replacement-0.2.8-py3-none-any.whl", "has_sig": false, "md5_digest": "1595ddc259b75cf2a2eefc2759193b03", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 13275, "upload_time": "2018-10-29T15:34:04", "url": "https://files.pythonhosted.org/packages/44/4f/3902b62ddc887f4aa00f6a312926cf59e2ac9654f2ced67e05c0433fa380/replacement-0.2.8-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "42dc5e5d41b50c31f35356782430eeff", "sha256": "2af726ab819f0c786f21dd4d6bfd4040e26427b9117ffdce1bc04e101cbf657a" }, "downloads": -1, "filename": "replacement-0.2.8.tar.gz", "has_sig": false, "md5_digest": "42dc5e5d41b50c31f35356782430eeff", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 12637, "upload_time": "2018-10-29T15:34:05", "url": "https://files.pythonhosted.org/packages/63/e4/53e8df19e05a06a18564d4867c8e9956d4f51b4e6e64da0e0ccf3250ad58/replacement-0.2.8.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "fdfc8e4faf0867310fa435e457c1ab7f", "sha256": "163d629634fbf8462ac07d0f543f271e8597c8dd97c510ce71f605f7ba85796f" }, "downloads": -1, "filename": "replacement-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "fdfc8e4faf0867310fa435e457c1ab7f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 13903, "upload_time": "2018-11-03T17:08:14", "url": "https://files.pythonhosted.org/packages/68/5f/0dc1ead0dfa667c4a29802b5927e7dbd3d0ed1155d35c67b82357bcfcd85/replacement-0.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "259def8fff8251f6ffdfbedbcb6a9c05", "sha256": "ab6de941d736f765e25a052855cecde2cff448775d9b991db2a66b73486aabfb" }, "downloads": -1, "filename": "replacement-0.3.0.tar.gz", "has_sig": false, "md5_digest": "259def8fff8251f6ffdfbedbcb6a9c05", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 12101, "upload_time": "2018-11-03T17:08:16", "url": "https://files.pythonhosted.org/packages/05/53/af8355d459d0bb29ce7b54b0944d7c57226de793336eca8bc75777c6ef2b/replacement-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "7a7fd67b7a9f9815d32a4ff5b2f6b8aa", "sha256": "a2b0d2eea239baeaf68e054c526f0d5b35cf154732cb61e582dc17a4fb458785" }, "downloads": -1, "filename": "replacement-0.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "7a7fd67b7a9f9815d32a4ff5b2f6b8aa", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 13910, "upload_time": "2018-11-15T17:24:19", "url": "https://files.pythonhosted.org/packages/8d/1d/be09e2fa421ce90424ebabd480385270ad37df3a752c4b4be9e405e5b7d3/replacement-0.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "151485b976dd4e900e761585889cf20d", "sha256": "9ef3a51de0fa8f24e01cb045afb60068cc119768fbf1974fda1dd3a8465e10f8" }, "downloads": -1, "filename": "replacement-0.3.1.tar.gz", "has_sig": false, "md5_digest": "151485b976dd4e900e761585889cf20d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 13339, "upload_time": "2018-11-15T17:24:21", "url": "https://files.pythonhosted.org/packages/ac/a4/824f7d472a16ce6a08aebc71df8778a0e53f08e4f537953743d0e5d37fe9/replacement-0.3.1.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "72287d278cca869b01b246660100e5ab", "sha256": "9026d23931544997f2dcc35050fc5da7c323c560f4c54f9dd131bb7e7839b6d6" }, "downloads": -1, "filename": "replacement-0.3.2-py3-none-any.whl", "has_sig": false, "md5_digest": "72287d278cca869b01b246660100e5ab", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 14858, "upload_time": "2018-11-16T19:45:45", "url": "https://files.pythonhosted.org/packages/3e/48/a460bdbd5f4f6643281995ce94b4b0b3140934994f4c336f5a118e4c014e/replacement-0.3.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0be259454a9bc7acd7e2d4b81fefdeb7", "sha256": "633831fd661c8da3173e1c282d57e0697c3ae83b267069df08ae7c72cfc84727" }, "downloads": -1, "filename": "replacement-0.3.2.tar.gz", "has_sig": false, "md5_digest": "0be259454a9bc7acd7e2d4b81fefdeb7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 14390, "upload_time": "2018-11-16T19:45:47", "url": "https://files.pythonhosted.org/packages/93/6e/15ad41f5483a74fb03183be06eba700f8d756a7421174bc460156246354a/replacement-0.3.2.tar.gz" } ], "0.3.3": [ { "comment_text": "", "digests": { "md5": "4bbf85d2605d0a20f801ffabc7e741b0", "sha256": "68072ecf420124fb146427c44bf7e6645505d2eee9fb484bd946e9e34b8e8291" }, "downloads": -1, "filename": "replacement-0.3.3-py3-none-any.whl", "has_sig": false, "md5_digest": "4bbf85d2605d0a20f801ffabc7e741b0", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 15156, "upload_time": "2018-11-20T18:36:20", "url": "https://files.pythonhosted.org/packages/ef/90/cdc7f57728d9deca47e9f88cd67b2aa602574b03ffd9c9aac6e32a4f9026/replacement-0.3.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "369352564d3ace86dcc631286cb9908a", "sha256": "7c1c30d1fc4e303b55d581c188801aa6c72f37e4492fc6630a5a291914f5b499" }, "downloads": -1, "filename": "replacement-0.3.3.tar.gz", "has_sig": false, "md5_digest": "369352564d3ace86dcc631286cb9908a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 14856, "upload_time": "2018-11-20T18:36:21", "url": "https://files.pythonhosted.org/packages/dc/9f/5846654a5b984b7487b468863253885d79b210b53cd1afad100abedc814c/replacement-0.3.3.tar.gz" } ], "0.3.5": [ { "comment_text": "", "digests": { "md5": "a3aa0034fa74554aa4b64caddc1b68e2", "sha256": "6ed66e7425a7f7d8a80bcc5e84646bb869ce0b8537f90c513bd211f7fba8edf6" }, "downloads": -1, "filename": "replacement-0.3.5-py3-none-any.whl", "has_sig": false, "md5_digest": "a3aa0034fa74554aa4b64caddc1b68e2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 16447, "upload_time": "2018-11-24T03:07:49", "url": "https://files.pythonhosted.org/packages/d7/5e/9c32cd214d12c6484f8b4dd717b4965d19ea73f63c4e3f0e1ea5fa4aaba1/replacement-0.3.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4ce0eab690ca8c6b7e5cb336ad3db16e", "sha256": "d594be6191915d581d412491762fe172f52e72e22e313e7bc1f16bb1b2f95b16" }, "downloads": -1, "filename": "replacement-0.3.5.tar.gz", "has_sig": false, "md5_digest": "4ce0eab690ca8c6b7e5cb336ad3db16e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 15961, "upload_time": "2018-11-24T03:07:51", "url": "https://files.pythonhosted.org/packages/61/08/7e476eb7d9b6cab0aee80810457f3ac22d39477ad29ca13098a875a38f18/replacement-0.3.5.tar.gz" } ], "0.3.6": [ { "comment_text": "", "digests": { "md5": "84ace24759f947df1a5709070f410fbb", "sha256": "3c0b5b74b078f8eac143014b1226c23572956c83ebf9f5da9453b1cc10155a7f" }, "downloads": -1, "filename": "replacement-0.3.6-py3-none-any.whl", "has_sig": false, "md5_digest": "84ace24759f947df1a5709070f410fbb", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 20813, "upload_time": "2019-03-21T03:11:17", "url": "https://files.pythonhosted.org/packages/f0/fd/f241c16cb5800eb72b98f32f72c866f8163faa5746cb6ad17e689e17777b/replacement-0.3.6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8957ce350e6414f71d365e47b5d04371", "sha256": "ed9ad3e51ac24332db541db12af533378a7828db2d53c262546404d6d993f116" }, "downloads": -1, "filename": "replacement-0.3.6.tar.gz", "has_sig": false, "md5_digest": "8957ce350e6414f71d365e47b5d04371", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 16121, "upload_time": "2019-03-21T03:11:19", "url": "https://files.pythonhosted.org/packages/37/92/2b46b293f651d24fb01bc9ab05a5be9c57b553f13ffada328375f88b0dac/replacement-0.3.6.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "84ace24759f947df1a5709070f410fbb", "sha256": "3c0b5b74b078f8eac143014b1226c23572956c83ebf9f5da9453b1cc10155a7f" }, "downloads": -1, "filename": "replacement-0.3.6-py3-none-any.whl", "has_sig": false, "md5_digest": "84ace24759f947df1a5709070f410fbb", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 20813, "upload_time": "2019-03-21T03:11:17", "url": "https://files.pythonhosted.org/packages/f0/fd/f241c16cb5800eb72b98f32f72c866f8163faa5746cb6ad17e689e17777b/replacement-0.3.6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8957ce350e6414f71d365e47b5d04371", "sha256": "ed9ad3e51ac24332db541db12af533378a7828db2d53c262546404d6d993f116" }, "downloads": -1, "filename": "replacement-0.3.6.tar.gz", "has_sig": false, "md5_digest": "8957ce350e6414f71d365e47b5d04371", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 16121, "upload_time": "2019-03-21T03:11:19", "url": "https://files.pythonhosted.org/packages/37/92/2b46b293f651d24fb01bc9ab05a5be9c57b553f13ffada328375f88b0dac/replacement-0.3.6.tar.gz" } ] }