{ "info": { "author": "Reuben Rusk", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# vartrix\n\nVartrix is about managing and automating parameters used in code. The name 'vartrix' is short for 'variable tricks'.\n\nYou might like vartrix if:\n\n* You're worried about the growing complexity of all the parameters in your code and you'd like to use a robust, scalable approach from the start.\n* Tracking all the parameters in your code has become difficult.\n* You need parameters to be traceable so you are confident that the right ones are used\n* You need to change parameters in a simple, robust and traceable way to ensure you haven't got an undetected coding error.\n* You need to be able to snapshot parameters and store them.\n* You've created ugly, fragile code to step through sets of parameters and run bits of code for each set.\n* You pass around a lot of parameters between classes in your code, making it bloated, difficult to maintain, and fragile\n\n\n## Quickstart tutorial - basic usage\n\nInstall using:\n\n```\npip install vartrix\n```\n\nThen in your code, import vartrix to get started:\n\n```python\nimport vartrix\n```\n\n### Containers\nA container is a dictionary-like object that contains a set of parameters. The keys are 'dotkeys'. For example, 'a.b.c', or 'subpackage.module.class.key'.\n\nThere are a few ways to set up containers. First, from a nested dictionary:\n```python\ndct = {'A': {'apple': 5, 'banana': 7, 'grape': 11},\n\t 'B': {'fig': 13, 'pear': 17, 'orange': 19}}\ncontainer = vartrix.Container(dct)\nprint(container)\n# {'A.apple': 5, 'A.banana': 7, 'A.grape': 11, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\n```\n\nFrom a flat ('dotkey') dictionary:\n```python\ndct = {'A.apple': 5, 'A.banana': 7, 'A.grape': 11,\n\t 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\ncontainer = vartrix.Container(dct)\nprint(container)\n# {'A.apple': 5, 'A.banana': 7, 'A.grape': 11, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\n```\n\nYou can use any dictionary to set up the Container.\n\n### Name spaces\n\nUsually, we set containers up using a Name_Space instance. You can use the default:\n```python\ncontainer = vartrix.get_container('example_name')\n```\n\nOr, set up your own:\n```python\nns = vartrix.Name_Space()\ncontainer = ns.create('example_name_2', dct=dct)\n```\n\nInitialise it with a dictionary, like the above, or load one into it. A short way to set up a container is like this, as new containers are created if required:\n```python\nns['tutorial_1'].load(dct)\n```\n\n\n### Views\nFor large containers with multiple levels, it's much easier to deal with a View of a specific set of the dotkeys. Views allow the values for those dotkeys to be accessed without the preceeding levels of the key. For example:\n\n```python\nview_A = vartrix.View(ns['tutorial_1'], dotkeys='A')\nprint(view_A)\n# {'apple': 5, 'banana': 7, 'grape': 11}\n```\n\nViews have both dictionary-style key access and attribute-style access:\n```python\nprint(view_A['apple'])\n# 5\nprint(view_A.apple)\n# 5\n```\n\nYou can use them in a class like this:\n```python\nclass B():\n\tdef __init__(self):\n\t\tself.params = vartrix.View(ns['tutorial_1'], dotkeys=['B'])\n\nb = B()\nprint(b.params)\n# {'fig': 13, 'pear': 17, 'orange': 19}\n```\n\nYou can pass in the object instead. It will automatically remove the package name or `__main__` prefix on the class names. It automatically includes base classes so inheritance works.\n\n```python\nclass A():\n\tdef __init__(self):\n\t\tself.params = vartrix.View(ns['tutorial_1'], obj=self)\n\t\t# Class A objects have signature 'tutorial_1.A'\na = A()\nprint(a.params)\n# {'apple': 5, 'banana': 7, 'grape': 11}\n```\n \nYou can use multiple dotkeys:\n```python\nclass Combined():\n\tdef __init__(self):\n\t\tself.params = vartrix.View(ns['tutorial_1'], dotkeys=['A', 'B'])\nc = Combined()\nprint(c.params)\n# {'A.apple': 5, 'A.banana': 7, 'A.grape': 11, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\n```\n\t\n\t\n### Remote updates\nThe views are automatically updated with changes from their associated container. Let's first make a view and get a reference to the container:\n\n```python\na = A()\ncontainer = ns['tutorial_1']\n```\n\nLet's update the 'A.apple' value using setitem style:\n```python\ncontainer['A.apple'] = 101\nprint(container['A.apple'])\n# 101\nprint(a.params['apple'])\n# 101\n```\n\nWe can use the 'set' method:\n```python\ncontainer.set('A.apple', 102)\nprint(container['A.apple'])\n# 102\n```python\n\nUse the `lset` method for dotkets as lists of strings:\n```python\ncontainer.lset(['A', 'apple'], 103)\nprint(container['A.apple'])\n# 103\n```\n\nAnd use the `dset` method to set a range of values using a dictionary of dotkeys:\n```python\n{'A.apple': 103, 'A.banana': 7, 'A.grape': 11, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\ncontainer.dset({'A.apple': 104, 'A.grape': 201})\nprint(container)\n{'A.apple': 104, 'A.banana': 7, 'A.grape': 201, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\n```\n\n### Preventing updating\nIf you don't want a view to update, set the `live` attribute to False, like this:\n```python\na.params.live = False\ncontainer.dset({'A.apple': 111, 'A.grape': 222})\nprint(container)\n# {'A.apple': 111, 'A.banana': 7, 'A.grape': 222, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\nprint(a.params)\n# {'apple': 104, 'banana': 7, 'grape': 201}\n```\n\nCall the `refresh()` method to manually refresh non-live Views. They automatically refresh when they are set back to live:\n```python\na.params.live = True\nprint(a.params)\n# {'apple': 111, 'banana': 7, 'grape': 222}\n```\n\n\nLive views stay up to date with the container, even when keys are added or removed:\n```python\nbackup = container.copy()\ndct = {'A.apple': 77, 'A.banana': 87, 'A.grape': 91, 'A.pineapple': 55,\n\t 'B.fig': 102, 'B.pear': 150, 'B.orange': 300}\ncontainer.load(dct)\nprint(a.params)\n# {'apple': 77, 'banana': 87, 'grape': 91, 'pineapple': 55}\ncontainer.load(backup)\nprint(a.params)\n# {'apple': 111, 'banana': 7, 'grape': 222}\n```\n\nTo only set values temporarily, use the context manager:\n```python\nprint(container)\n# {'A.apple': 111, 'A.banana': 7, 'A.grape': 222, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\nd = {'A.apple': 555, 'B.orange': -7}\nwith container.context(d):\n\tprint(container)\n\t# {'A.apple': 555, 'A.banana': 7, 'A.grape': 222, 'B.fig': 13, 'B.pear': 17, 'B.orange': -7}\nprint(container)\n# {'A.apple': 111, 'A.banana': 7, 'A.grape': 222, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\n```\n\n\n### View updates\nSometimes, it's more convenient to set values on a view. This works in a similar way. Values set on a live view are reflected in the container, and all other linked views.\n\nUse a setitem style:\n```python\na.params['apple'] = 1001\nprint(container['A.apple'])\n# 1001\nprint(a.params['apple'])\n# 1001\n```\n\nThe `set` method:\n```python\na.params.set('apple', 1002)\nprint(container['A.apple'])\n# 1002\nprint(a.params['apple'])\n# 1002\n```\n\nOr the `dset` method for multiple key-value pairs:\n```python\na.params.dset({'apple': 1003, 'grape': 2002})\nprint(container)\n# {'A.apple': 1003, 'A.banana': 7, 'A.grape': 2002, 'B.fig': 13, 'B.pear': 17, 'B.orange': 19}\nprint(a.params)\n# {'apple': 1003, 'banana': 7, 'grape': 2002}\n```\n\t\nUse the context manager to set values temporarily:\n```python\nprint(a.params)\n# {'apple': 1003, 'banana': 7, 'grape': 2002}\nd2 = {'apple': 400, 'grape': 1000}\nwith a.params.context(d2):\n\tprint(a.params)\n\t# {'apple': 400, 'banana': 7, 'grape': 1000}\nprint(a.params)\n# {'apple': 1003, 'banana': 7, 'grape': 2002}\n```\n\n## Quickstart tutorial - automation\n\n### Setup\n\nLet's set up a function to make a very simple Container first:\n```python\nimport vartrix\n\nns = vartrix.Name_Space()\n\ndef setup_container():\n dct = {'A': {'apple': 1},\n 'B': {'orange': 2, 'fig': 3}}\n container = ns['tutorial_2']\n container.load(dct)\n```\n\nLet's run it and check it's worked:\n\n```python\nsetup_container()\nprint(ns['tutorial_2'])\n# {'A.apple': 1, 'B.orange': 2, 'B.fig': 3}\n```\n\n### Sequences\nNow, let's create a a set of automation parameters. They need to be a nested dictionary structure. We'll use a Yaml file to set them up.\n\ntutorial_2.yml\n```yaml\nset_1:\n aliases:\n alias_1: A.apple\n alias_2: B.orange\n alias_3: B.fig\n vectors:\n vec_1:\n alias_1: [5, 10, 15]\n vec_2:\n labels: ['a', 'b', 'c']\n alias_2: [ 2, 3, 4]\n alias_3: [ 6, 7, 8]\n sequences:\n seq_1:\n method_a: [vec_1, vec_2]\n```\n\nThere are some key things about the structure:\n\n* The highest level is for each set. Only one set is run at a time. Each `set` is labeled by its its key. In this case, we have one set called `set_1`.\n* Each set needs three keys:\n * **aliases:** A dictionary where keys are aliases - shorter names for possibly long entries in the container - and the values are the corresponding container keys. The values need to exist as keys in the container.\n\t* **vectors:** Dictionary where each key-value pair specifies a series of values. The aliases must exist in the `aliases` dictionary. There are multiple formats, as described below.\n\t* **sequences:** A dictionary where each key is the name of a sequence. Inside each sequence, there are keys that correspond to method names in the class that we're going to use for the automation. The values are lists of vector names that must exist in the `vectors` dictionary. The way they work is described below.\n\t\n#### How sequences work\nInside each sequence is a list of methods (the keys) and their corresponding list of vector names. The automator takes the list of vector names and interates over their values in a nested fashion. For example, `[vec_1, vec_2]` means the outer loop iterates over the set of values in `vec_1`, while the inner loop iterates over the values in `vec_2`. There is no limit to how many vectors you use, but since the total number of steps in the overall sequence grows exponentially, don't use too many.\n\n#### Vectors\nVectors specify what values to iterate over for one or more aliases. There are many ways to specify them, as described below.\n\n**Simple vector.** Labels of [0, 1, 2] will be automatically created. The `style` key-value pair is optional for this style.\n```yaml\n vec_1:\n style: value_lists\n alias_1: [5, 10, 15]\n```\n\n**Value lists.** Here, we'll specify the labels in a separate vector. On the first iteration, the first values of each vector will be used. The second iteration will use the second values, and so on. The `style` key-value pair is optional for this style.\n```yaml\n vec_2:\n style: value_lists\n labels: ['a', 'b', 'c']\n alias_2: [ 2, 3, 4]\n alias_3: [ 6, 7, 8]\n```\n\n**Value dictionaries.** We could achieve the same `vec_2` as above using the format below:\n```yaml\n vec_2:\n style: value_dictionaries\n a: {alias_2: 2, alias_3: 6},\n b: {alias_2: 3, alias_3: 7},\n c: {alias_2: 4, alias_3: 8},\n```\n\n**Csv file.** We could achieve the same `vec_2` as above using a csv file combined with the format below. The filename is joined with the path at `vartrix.automate.root`, which can be set by calling `vartrix.automate.set_root(path)`.\n```yaml\n vec_2:\n style: csv\n filename: tutorial_2.csv\n```\n\ntutorial_2.csv:\n\nindex | alias_2 | alias_3\n----- | ------- | -------\n'a' | 2 | 6\n'b' | 3 | 7\n'c' | 4 | 8\n\n\n#### Initialisation\n\nTo initialise, just pass in a Container instance and the filename to load from:\n\n```python\nimport os\nroot = os.path.dirname(__file__)\nfname = os.path.join(root, 'tutorial_2.yml')\nautomator = vartrix.Automator(ns['tutorial_2'], fname)\n```\n\n### Automated classes\n\nThe vartrix Automator calls the method(s) specified in each sequence at each iteration through the nested vector loops. In addition, there are several methods that provide hooks:\n\n* prepare(): Called at the start of the set\n* prepare_sequence(seq_name): Called at the start of each sequence\n* prepare_method(method_name): Called before starting to call `method_name` at each iteration\n* method_name(seq_name, val_dct, label_dct): The only required method - the name must match that in the sequence dictionary.\n* finish_method(method_name): Called after after calling `method_name` at each iteration\n* finish_sequence(seq_name): Called at the end of each sequence\n* finish(): Called at the end of the set\n\nFor this tutorial, we'll create a simple automated class like this:\n\n```python\nclass Automated():\n def __init__(self):\n self.params = vartrix.View(ns['tutorial_2'], dotkeys=['A', 'B'])\n \n def prepare(self):\n print('preparing...')\n\n def prepare_sequence(self, seq_name):\n print('running sequence: ' + seq_name)\n\n def prepare_method(self, method_name):\n print('running method: ' + method_name)\n\n def method_a(self, seq_name, val_dct, label_dct):\n print('calling method_a:')\n print('current labels: ' + str(label_dct))\n print('current params: ' + str(self.params))\n\n def finish_method(self, method_name):\n print('finishing method: ' + method_name)\n\n def finish_sequence(self, seq_name):\n print('finishing sequence: ' + seq_name)\n\n def finish(self):\n self.finish = True\n```\n\n### Execution\n\nNow for the easy part. We can simply create an instance of our Automated class and pass it into the automator with the set name.\n\n```python\nautomated = Automated()\nautomator.run('set_1', automated)\n```\n\nThe output looks like this:\n```python\npreparing...\nrunning sequence: seq_1\nrunning method: method_a\ncalling method_a:\ncurrent labels: {'vec_1': 0, 'vec_2': 'a'}\ncurrent params: {'apple': 5, 'orange': 2, 'fig': 6}\ncalling method_a:\ncurrent labels: {'vec_1': 0, 'vec_2': 'b'}\ncurrent params: {'apple': 5, 'orange': 3, 'fig': 7}\ncalling method_a:\ncurrent labels: {'vec_1': 0, 'vec_2': 'c'}\ncurrent params: {'apple': 5, 'orange': 4, 'fig': 8}\ncalling method_a:\ncurrent labels: {'vec_1': 1, 'vec_2': 'a'}\ncurrent params: {'apple': 10, 'orange': 2, 'fig': 6}\ncalling method_a:\ncurrent labels: {'vec_1': 1, 'vec_2': 'b'}\ncurrent params: {'apple': 10, 'orange': 3, 'fig': 7}\ncalling method_a:\ncurrent labels: {'vec_1': 1, 'vec_2': 'c'}\ncurrent params: {'apple': 10, 'orange': 4, 'fig': 8}\ncalling method_a:\ncurrent labels: {'vec_1': 2, 'vec_2': 'a'}\ncurrent params: {'apple': 15, 'orange': 2, 'fig': 6}\ncalling method_a:\ncurrent labels: {'vec_1': 2, 'vec_2': 'b'}\ncurrent params: {'apple': 15, 'orange': 3, 'fig': 7}\ncalling method_a:\ncurrent labels: {'vec_1': 2, 'vec_2': 'c'}\ncurrent params: {'apple': 15, 'orange': 4, 'fig': 8}\nfinishing method: method_a\nfinishing sequence: seq_1\n```\n\nThe values in the Container are changed automatically by the Automator before calling `method_a`. But, the automator also passes in `val_dct`, a dictionary of automated key-value pairs, in case they are convenient. It's also often desirable to have shorter, simpler labels at each iteration for each vector, and the automator passes in `label_dct` for that purpose as well.\n\nIf we want to change the way we automate the parameters, now we only need to change the specification in our yaml file (`tutorial_2.yml`) - there's no need for manual coding of the automation. This approach has a number of advantages:\n\n* Faster creation of iterative sequences\n* Fewer mistakes\n* Easier management of parameters\n* Full traceability of how parameters are changed\n* Flexble, loosely coupled code. The classes that use the values in the Container need no knowledge of the automation, and the automation needs no knowledge of them.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://github.com/pythoro/vartrix/archive/v0.0.3.zip", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/pythoro/vartrix.git", "keywords": "PARAMETERS,VARIABLES,PARAMETRIC,AUTOMATION,AUTOMATE", "license": "", "maintainer": "", "maintainer_email": "", "name": "vartrix", "package_url": "https://pypi.org/project/vartrix/", "platform": "", "project_url": "https://pypi.org/project/vartrix/", "project_urls": { "Download": "https://github.com/pythoro/vartrix/archive/v0.0.3.zip", "Homepage": "https://github.com/pythoro/vartrix.git" }, "release_url": "https://pypi.org/project/vartrix/0.0.3/", "requires_dist": null, "requires_python": "", "summary": "Easily manange and automate variables and parameters.", "version": "0.0.3" }, "last_serial": 5806372, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "a97c1caeda2a186ff8430a219cd4b48b", "sha256": "012cca1c5d3de085e9b56385f223c4ba9475897e116ce6a92e163b36407695e0" }, "downloads": -1, "filename": "vartrix-0.0.1.tar.gz", "has_sig": false, "md5_digest": "a97c1caeda2a186ff8430a219cd4b48b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9552, "upload_time": "2019-09-09T08:48:25", "url": "https://files.pythonhosted.org/packages/d5/89/61f90b88c0fd0cd5d857f9ee6f8883995750222c35cb62a3f65d0a535021/vartrix-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "dea0d4b64f2236e0778c32fc0ff56b71", "sha256": "f1db670e2a2541b44081771c9401c238b5f2d6e3bb975d88bb4446ae4361a20e" }, "downloads": -1, "filename": "vartrix-0.0.2.tar.gz", "has_sig": false, "md5_digest": "dea0d4b64f2236e0778c32fc0ff56b71", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13771, "upload_time": "2019-09-09T09:16:05", "url": "https://files.pythonhosted.org/packages/ae/d1/448805cda51f18056839bc3d57ecad1e85c56ee55e379939c414dba2a198/vartrix-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "48ff14f73527d8462089cf622080071f", "sha256": "3df6f7019d938750ed0158e007d1208cf8e0ce6cec185db7e7e3fe30f0eac716" }, "downloads": -1, "filename": "vartrix-0.0.3.tar.gz", "has_sig": false, "md5_digest": "48ff14f73527d8462089cf622080071f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20182, "upload_time": "2019-09-10T01:39:16", "url": "https://files.pythonhosted.org/packages/93/a8/05810ce27342b0b498d0a8df96400c1694115deaed5c45b9c85d9b9b1f19/vartrix-0.0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "48ff14f73527d8462089cf622080071f", "sha256": "3df6f7019d938750ed0158e007d1208cf8e0ce6cec185db7e7e3fe30f0eac716" }, "downloads": -1, "filename": "vartrix-0.0.3.tar.gz", "has_sig": false, "md5_digest": "48ff14f73527d8462089cf622080071f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20182, "upload_time": "2019-09-10T01:39:16", "url": "https://files.pythonhosted.org/packages/93/a8/05810ce27342b0b498d0a8df96400c1694115deaed5c45b9c85d9b9b1f19/vartrix-0.0.3.tar.gz" } ] }