{ "info": { "author": "Robertus Johansyah", "author_email": "kororola@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: POSIX :: BSD", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "conff\n=====\n\nSimple config parser with evaluator library.\n\n.. image:: https://badge.fury.io/py/conff.svg\n :target: https://badge.fury.io/py/conff\n\n.. image:: https://travis-ci.com/kororo/conff.svg?branch=master\n :target: https://travis-ci.com/kororo/conff\n\n.. image:: https://coveralls.io/repos/github/kororo/conff/badge.svg?branch=master\n :target: https://coveralls.io/github/kororo/conff?branch=master\n\n.. image:: https://api.codeclimate.com/v1/badges/c476e9c6bfe505bc4b4d/maintainability\n :target: https://codeclimate.com/github/kororo/conff/maintainability\n :alt: Maintainability\n\n.. image:: https://badges.gitter.im/kororo-conff.png\n :target: https://gitter.im/kororo-conff\n :alt: Gitter\n\n\nWhy Another Config Parser Module?\n---------------------------------\n\nThis project inspired of the necessity complex config in a project. By means complex:\n\n- Reusability\n\n - Import values from file\n - Reference values from other object\n\n- Secure\n\n - Encrypt/decrypt sensitive values\n\n- Flexible\n\n - Make logical expression to derive values\n - Combine with `jinja2 `_ template based\n\n- Powerful\n\n - Add custom functions in Python\n - Link name data from Python\n\nFeedback and Discussion\n-----------------------\n\nCome to Gitter channel to discuss, pass any feedbacks and suggestions. If you like to be contributor, please do let me know.\n\nImportant Notes\n---------------\n\nParsing Order\n^^^^^^^^^^^^^\n\nconff will only parse and resolve variable/names top to bottom order. Please ensure you arrange your configuration\nin the same manner, there is no auto-dependencies resolver to handle complex and advanced names currently.\n\ndict vs collections.OrderedDict\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn Python 3.5, the dict data type has inconsistent ordering, it is **STRONGLY** recommended to use **OrderedDict** if\nyou manually parse object. If you load from YAML file, the library already handled it. The reason of order is important,\nthis due to simplification and assumption of order execution. The library will parse the values from top to bottom as\nper order in the key-value dictionary.\n\nInstall\n-------\n\n.. code:: bash\n\n [sudo] pip install conff\n\nBasic Usage\n-----------\n\nTo get very basic parsing:\n\nSimple parse\n^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.parse({'math': '1 + 3'})\n assert r == {'math': 4}\n\nLoad YAML file\n^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.load('path_of_file.yml')\n\nTemplate based config\n^^^^^^^^^^^^^^^^^^^^^\n\nUsing `jinja2 `_ to craft more powerful config.\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.parse('F.template(\"{{ 1 + 2 }}\")')\n assert r == 3\n\n\nExamples\n--------\n\nMore advances examples:\n\nParse with simple expression\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.parse('1 + 2')\n assert r == 3\n\nParse object\n^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.parse({\"math\": \"1 + 2\"})\n assert r == {'math': 3}\n\nIgnore expression (declare it as string)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = conff.parse('\"1 + 2\"')\n assert r == '1 + 2'\n\nParse error behaviours\n^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser()\n r = p.parse({'math': '1 / 0'})\n # Exception raised\n # ZeroDivisionError: division by zero\n\n\nimport files\n^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n ## y1.yml\n # shared_conf: 1\n ## y2.yml\n # conf: F.inc('y1.yml')\n\n p = conff.Parser()\n r = p.load('y2.yml')\n assert r == {'conf': {'shared_conf': 1}}\n\nParse with functions\n^^^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n def fn_add(a, b):\n return a + b\n p = conff.Parser(fns={'add': fn_add})\n r = p.parse('F.add(1, 2)')\n assert r == 3\n\nParse with names\n^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n p = conff.Parser(names={'a': 1, 'b': 2})\n r = conff.parse('a + b')\n assert r == 3\n\nParse with extends\n^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n data = {\n 't1': {'a': 'a'},\n 't2': {\n 'F.extend': 'R.t1',\n 'b': 'b'\n }\n }\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'t1': {'a': 'a'}, 't2': {'a': 'a', 'b': 'b'}}\n\nParse with updates\n^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n data = {\n 't1': {'a': 'a'},\n 't2': {\n 'b': 'b',\n 'F.update': {\n 'c': 'c'\n },\n }\n }\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'t1': {'a': 'a'}, 't2': {'b': 'b', 'c': 'c'}}\n\nParse with extends and updates\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: python\n\n import conff\n data = {\n 't1': {'a': 'a'},\n 't2': {\n 'F.extend': 'R.t1',\n 'b': 'b',\n 'F.update': {\n 'a': 'A',\n 'c': 'c'\n },\n }\n }\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'t1': {'a': 'a'}, 't2': {'a': 'A', 'b': 'b', 'c': 'c'}}\n\nCreate a list of values\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThis creates a list of floats, similar to numpy.linspace\n\n.. code:: python\n\n import conff\n data = {'t2': 'F.linspace(0, 10, 5)'}\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'t2': [0.0, 2.5, 5.0, 7.5, 10.0]}\n\nThis also creates a list of floats, but behaves like numpy.arange (although\nslightly different in that it is inclusive of the endpoint).\n\n.. code:: python\n\n import conff\n data = {'t2': 'F.arange(0, 10, 2)'}\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'t2': [0, 2, 4, 6, 8, 10]}\n\nParse with for each\n^^^^^^^^^^^^^^^^^^^\n\nOne can mimic the logic of a for loop with the following example\n\n.. code:: python\n\n import conff\n data = {'t1': 2,\n 'F.foreach': {\n 'values': 'F.linspace(0, 10, 2)',\n # You have access to loop.index, loop.value, and loop.length\n # within the template, as well as all the usual names\n 'template': {\n '\"test%i\"%loop.index': 'R.t1*loop.value',\n 'length': 'loop.length'\n }\n }\n }\n p = conff.Parser()\n r = p.parse(data)\n assert r == {'length': 3, 't1': 2, 'test0': 0.0, 'test1': 10.0, 'test2': 20.0}\n\nEncryption\n----------\n\nThis section to help you to quickly generate encryption key, initial encrypt values and test to decrypt the value.\n\n.. code:: python\n\n import conff\n # generate key, save it somewhere safe\n names = {'R': {'_': {'etype': 'fernet'}}}\n etype = conff.generate_key(names)()\n # or just\n ekey = conff.generate_key()('fernet')\n\n # encrypt data\n # BIG WARNING: this should be retrieved somewhere secured for example in ~/.secret\n # below just for example purposes\n ekey = 'FOb7DBRftamqsyRFIaP01q57ZLZZV6MVB2xg1Cg_E7g='\n names = {'R': {'_': {'etype': 'fernet', 'ekey': ekey}}}\n # gAAAAABbBBhOJDMoQSbF9jfNgt97FwyflQEZRxv2L2buv6YD_Jiq8XNrxv8VqFis__J7YlpZQA07nDvzYwMU562Mlm978uP9BQf6M9Priy3btidL6Pm406w=\n encrypted_value = conff.encrypt(names)('ACCESSSECRETPLAIN1234')\n\n # decrypt data\n ekey = 'FOb7DBRftamqsyRFIaP01q57ZLZZV6MVB2xg1Cg_E7g='\n names = {'R': {'_': {'etype': 'fernet', 'ekey': ekey}}}\n encrypted_value = 'gAAAAABbBBhOJDMoQSbF9jfNgt97FwyflQEZRxv2L2buv6YD_Jiq8XNrxv8VqFis__J7YlpZQA07nDvzYwMU562Mlm978uP9BQf6M9Priy3btidL6Pm406w='\n conff.decrypt(names)(encrypted_value)\n\nReal World Examples\n-------------------\n\nAll the example below located in `data directory `_.\nImagine you start an important project, your code need to analyse image/videos which involves workflow\nwith set of tasks with AWS Rekognition. The steps will be more/less like this:\n\n 1. Read images/videos from a specific folder, if images goes to (2), if videos goes to (3).\n\n 2. Analyse the images with AWS API, then goes (4)\n\n 3. Analyse the videos with AWS API, then goes (4)\n\n 4. Write the result back to JSON file, finished\n\nThe configuration required:\n\n 1. Read images/videos (where is the folder)\n\n 2. Analyse images (AWS API credential and max resolution for image)\n\n 3. Analyse videos (AWS API credential and max resolution for video)\n\n 4. Write results (where is the result should be written)\n\n1. Without conff library\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nFile: `data/sample_config_01.yml `_\n\nWhere it is all started, if we require to store the configuration as per normally, it should be like this.\n\n.. code:: yaml\n\n job:\n read_image:\n # R01\n root_path: /data/project/images_and_videos/\n analyse_image:\n # R02\n api_cred:\n region_name: ap-southeast-2\n aws_access_key_id: ACCESSKEY1234\n # R03\n aws_secret_access_key: ACCESSSECRETPLAIN1234\n max_res: [1024, 768]\n analyse_video:\n # R04\n api_cred:\n region_name: ap-southeast-2\n aws_access_key_id: ACCESSKEY1234\n aws_secret_access_key: ACCESSSECRETPLAIN1234\n max_res: [800, 600]\n write_result:\n # R05\n output_path: /data/project/result.json\n\n.. code:: python\n\n import yaml\n with open('data/sample_config_01.yml') as stream:\n r1 = yaml.safe_load(stream)\n\nNotes:\n\n - R01: The subpath of \"/data/project\" is repeated between R01 and R05\n - R02: api_cred is repeatedly defined with R04\n - R03: the secret is plain visible, if this stored in GIT, it is pure disaster\n\n2. Fix the repeat\n^^^^^^^^^^^^^^^^^\n\nFile: `data/sample_config_02.yml `_\n\nRepeating values/configuration is bad, this could potentially cause human mistake if changes made is not\nconsistently applied in all occurences.\n\n.. code:: yaml\n\n # this can be any name, as long as not reserved in Python\n shared:\n project_path: /data/project\n aws_cred:\n region_name: ap-southeast-2\n aws_access_key_id: ACCESSKEY1234\n # F03\n aws_secret_access_key: F.decrypt('gAAAAABbBBhOJDMoQSbF9jfNgt97FwyflQEZRxv2L2buv6YD_Jiq8XNrxv8VqFis__J7YlpZQA07nDvzYwMU562Mlm978uP9BQf6M9Priy3btidL6Pm406w=')\n\n job:\n read_image:\n # F01\n root_path: R.shared.project_path + '/images_and_videos/'\n analyse_image:\n # F02\n api_cred: R.shared.aws_cred\n max_res: [1024, 768]\n analyse_video:\n # F04\n api_cred: R.shared.aws_cred\n max_res: [800, 600]\n write_result:\n # F05\n output_path: R.shared.project_path + '/result.json'\n\n.. code:: python\n\n import conff\n # ekey is the secured encryption key\n # WARNING: this is just demonstration purposes\n ekey = 'FOb7DBRftamqsyRFIaP01q57ZLZZV6MVB2xg1Cg_E7g='\n r2 = conff.load(fs_path='data/sample_config_02.yml', params={'ekey': ekey})\n\nNotes:\n\n - F01: it is safe if the prefix '/data/project' need to be changed, it will automatically changed for F05\n - F02: no more duplicated config with F04\n - F03: it is secured to save this to GIT, as long as the encryption key is stored securely somewhere in server such\n as ~/.secret\n\n3. Optimise to the extreme\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFile: `data/sample_config_03.yml `_\n\nThis is just demonstration purposes to see the full capabilities of this library.\n\n.. code:: yaml\n\n # this can be any name, as long as not reserved in Python\n shared:\n project_path: /data/project\n analyse_image_video:\n api_cred:\n region_name: ap-southeast-2\n aws_access_key_id: ACCESSKEY1234\n aws_secret_access_key: F.decrypt('gAAAAABbBBhOJDMoQSbF9jfNgt97FwyflQEZRxv2L2buv6YD_Jiq8XNrxv8VqFis__J7YlpZQA07nDvzYwMU562Mlm978uP9BQf6M9Priy3btidL6Pm406w=')\n max_res: [1024, 768]\n job:\n read_image:\n root_path: R.shared.project_path + '/images_and_videos/'\n analyse_image: R.shared.analyse_image_video\n analyse_video:\n F.extend: R.shared.analyse_image_video\n F.update:\n max_res: [800, 600]\n write_result:\n output_path: R.shared.project_path + '/result.json'\n\nFor completeness, ensuring data is consistent and correct between sample_config_01.yml, sample_config_02.yml\nand sample_config_03.yml.\n\n.. code:: python\n\n # nose2 conff.test.ConffTestCase.test_sample\n fs_path = 'data/sample_config_01.yml'\n with open(fs_path) as stream:\n r1 = yaml.safe_load(stream)\n fs_path = 'data/sample_config_02.yml'\n ekey = 'FOb7DBRftamqsyRFIaP01q57ZLZZV6MVB2xg1Cg_E7g='\n r2 = conff.load(fs_path=fs_path, params={'ekey': ekey})\n fs_path = 'data/sample_config_03.yml'\n r3 = conff.load(fs_path=fs_path, params={'ekey': ekey})\n self.assertDictEqual(r1['job'], r2['job'], 'Mismatch value')\n self.assertDictEqual(r2['job'], r3['job'], 'Mismatch value')\n\nTest\n----\n\nTo test this project:\n\n.. code:: bash\n\n # default test\n nose2\n\n # test with coverage\n nose2 --with-coverage\n\n # test specific\n nose2 conff.test.ConffTestCase.test_sample\n\nTODO\n----\n\n- [X] Setup basic necessity\n\n - [X] Stop procrastinating\n - [X] Project registration in pypi\n - [X] Create unit tests\n - [X] Setup travis\n - [X] Setup coveralls\n\n- [ ] Add more support on `Python versions `_\n\n - [ ] 2.7\n - [ ] 3.4\n - [X] 3.5\n - [X] 3.6\n\n- [ ] Features\n\n - Wish List Features now moved to `wiki page `_.\n\n- [ ] Improve docs\n\n - [ ] Add more code comments and visibilities\n - [ ] Make github layout code into two left -> right\n - [X] Put more examples\n - [ ] Setup readthedocs\n - [ ] Add code conduct, issue template into git project.\n - [ ] Add information that conff currently accept YML and it not limited, it can take any objects\n\n\nOther Open Source\n-----------------\n\nThis project uses other awesome projects:\n\n- `cryptography `_\n- `jinja2 `_\n- `munch `_\n- `simpleeval `_\n- `yaml `_\n\nWho uses conff?\n---------------\n\nPlease send a PR to keep the list growing, if you may please add your handle and company.\n", "description_content_type": "", "docs_url": null, "download_url": "https://github.com/kororo/conff/tarball/0.5.0", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/kororo/conff", "keywords": "config", "license": "", "maintainer": "", "maintainer_email": "", "name": "conff", "package_url": "https://pypi.org/project/conff/", "platform": "", "project_url": "https://pypi.org/project/conff/", "project_urls": { "Download": "https://github.com/kororo/conff/tarball/0.5.0", "Homepage": "https://github.com/kororo/conff" }, "release_url": "https://pypi.org/project/conff/0.5.0/", "requires_dist": null, "requires_python": "", "summary": "Simple config parser with evaluator library.", "version": "0.5.0" }, "last_serial": 3930907, "releases": { "0.3.0": [ { "comment_text": "", "digests": { "md5": "c44fff2311aee1a8505ccf84ff963768", "sha256": "4f62055f910feaee524e0bcfdd0d2fc3bccf7655ac1cff14a19d274e39c1a51b" }, "downloads": -1, "filename": "conff-0.3.0.tar.gz", "has_sig": false, "md5_digest": "c44fff2311aee1a8505ccf84ff963768", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6442, "upload_time": "2018-05-14T00:26:34", "url": "https://files.pythonhosted.org/packages/ce/02/90488595a15c9da7a3d188e337be5cba7f85b0c9575a28d9e93d3043dd7f/conff-0.3.0.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "57176a7ab34b013d73fc5cf02e71ce13", "sha256": "123975006cb4e7b5567b8de337716db3930249048f232362e5e5a59ffd52e212" }, "downloads": -1, "filename": "conff-0.3.2.tar.gz", "has_sig": false, "md5_digest": "57176a7ab34b013d73fc5cf02e71ce13", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6726, "upload_time": "2018-05-17T04:30:46", "url": "https://files.pythonhosted.org/packages/9d/60/b8b84d4db9237605f2b8c9ca46bd7d0da47dc0df2ba61363844f263776cd/conff-0.3.2.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "852f60297c008331045733abbe61cef4", "sha256": "70107db2f0e4a6fab05c30046593fe3f5fec2b5263873f68f88a13ce1c644f9d" }, "downloads": -1, "filename": "conff-0.4.1.tar.gz", "has_sig": false, "md5_digest": "852f60297c008331045733abbe61cef4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10333, "upload_time": "2018-05-22T15:06:03", "url": "https://files.pythonhosted.org/packages/b6/95/cdd3b61eebfbc9e7aa1661920d69cb23e7408e20cb7120c8b5159ffe18e5/conff-0.4.1.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "b46cf516984128ca3c81831c6201e0a7", "sha256": "b4b8ff7d5d6abf25366293253be7b1b898a8d29c401cb434313b4d4957c8164c" }, "downloads": -1, "filename": "conff-0.5.0.tar.gz", "has_sig": false, "md5_digest": "b46cf516984128ca3c81831c6201e0a7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15575, "upload_time": "2018-06-05T05:37:18", "url": "https://files.pythonhosted.org/packages/fa/34/bdc82e66f7bbcdbcb42a0abbec7c94fb533391c2ed05ea48358b0f750067/conff-0.5.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b46cf516984128ca3c81831c6201e0a7", "sha256": "b4b8ff7d5d6abf25366293253be7b1b898a8d29c401cb434313b4d4957c8164c" }, "downloads": -1, "filename": "conff-0.5.0.tar.gz", "has_sig": false, "md5_digest": "b46cf516984128ca3c81831c6201e0a7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15575, "upload_time": "2018-06-05T05:37:18", "url": "https://files.pythonhosted.org/packages/fa/34/bdc82e66f7bbcdbcb42a0abbec7c94fb533391c2ed05ea48358b0f750067/conff-0.5.0.tar.gz" } ] }