{ "info": { "author": "Mital Ashok", "author_email": "mital.vaja@googlemail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries" ], "description": "static\\_variables\r\n=================\r\n\r\nStatic variables for Python\r\n\r\n NOTE:\r\n\r\n This is still very much a work in progress, and will segfault if you\r\n give it anything that is mildly complex. It will probably not work\r\n on any implementation except CPython.\r\n\r\nUsage\r\n-----\r\n\r\n``static_variables``\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\n.. code:: python\r\n\r\n from static_variables import resolve_static\r\n \r\n @resolve_static(static_variables={'counter': 0})\r\n def f():\r\n counter += 1\r\n return counter\r\n \r\n print(f()) # 1\r\n print(f()) # 2\r\n print(f()) # 3\r\n\r\nThe signature for ``static_variables`` is ``Mapping[str, Any]``, where\r\nthe key is the name of the variable and the value is the initial value.\r\n\r\nAlso note that static variables will override global, nonlocal and local\r\nvariables with the same name.\r\n\r\nSet the value to ``static_variables.NO_VALUE`` to have no value in the\r\nbeginning:\r\n\r\n.. code:: python\r\n\r\n from static_variables import resolve_static, NO_VALUE\r\n \r\n @resolve_static(staic_variables={'value': NO_VALUE})\r\n def get_value():\r\n try:\r\n return value\r\n except NameError:\r\n value = could_be_anything()\r\n # `value` could also be `None`, so `None`\r\n # is not a sensible default.\r\n return value\r\n \r\n get_value() # Runs `could_be_anything`\r\n get_value() # Return the static value\r\n\r\n``static``\r\n~~~~~~~~~~\r\n\r\n.. code:: python\r\n\r\n from static_variables import static, resolve_static\r\n \r\n # You don't really need to import `static`, it just stops\r\n # IDEs from complaining.\r\n \r\n @resolve_static\r\n def f(to_add=None):\r\n ls = static([])\r\n if to_add is not None:\r\n ls.append(to_add)\r\n return ls\r\n \r\n ls = f()\r\n f(3)\r\n assert ls == [3] # True\r\n assert ls is f()\r\n\r\nSince Python variables are more like name tags, ``static`` will only\r\nreally work well for mutable objects, like ``list``\\ s or ``set``\\ s.\r\n\r\nFor example, the following does not work:\r\n\r\n.. code:: python\r\n\r\n @resolve_static\r\n def f():\r\n counter = static(0)\r\n counter += 1\r\n return counter\r\n \r\n assert f() == 1 # True\r\n assert f() == 2 # False\r\n\r\nYou would have to use the ``static_variables`` argument to achieve this.\r\n\r\nThe static variable will always have the same ``id``. They will refer to\r\nthe same object, and is stored at the end of a function's\r\n``function.__code__.co_consts``\r\n\r\nEmpty set literals\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nSince sets came after dictionaries, the ``{}`` literal is an empty\r\ndictionary. This changes that.\r\n\r\n.. code:: python\r\n\r\n @resolve_static(empty_set_literal=True)\r\n def f():\r\n return {}\r\n \r\n assert f() == set() # True\r\n assert f() != {} # True; {} is dict() in the outer scope.\r\n\r\nYou can also use ``EMPTY_SET`` to avoid turning all ``{}`` into empty\r\nsets.\r\n\r\n.. code:: python\r\n\r\n from static_variables import resolve_static, EMPTY_SET\r\n \r\n # Again, you don't need to import EMPTY_SET.\r\n # It just stops IDEs from complaining.\r\n \r\n @resolve_static(empty_set_literal=False)\r\n def f():\r\n my_dict = {}\r\n my_set = EMPTY_SET # Equivalent to `set()` but faster.\r\n return type(my_dict), type(my_set)\r\n\r\n assert f() == (dict, set) # True\r\n\r\nSpeed?\r\n------\r\n\r\nIt would actually be faster to use ``static``, as it delegates some\r\nprocessing to declaration time, instead of run time.\r\n\r\nTake these two snippets:\r\n\r\n.. code:: python\r\n\r\n def product_4(it):\r\n return itertools.product(it, repeat=4)\r\n \r\n @resolve_static\r\n def static_product_4(it):\r\n return static(itertools.product)(it, repeat=4)\r\n\r\nAnd their disassembly:\r\n\r\n::\r\n\r\n product_4(it)\r\n 0 LOAD_GLOBAL 0 (itertools)\r\n 2 LOAD_ATTR 1 (product)\r\n 4 LOAD_FAST 0 (it)\r\n 6 LOAD_CONST 1 (4)\r\n 8 LOAD_CONST 2 (('repeat',))\r\n 10 CALL_FUNCTION_KW 2\r\n 12 RETURN_VALUE\r\n\r\n::\r\n\r\n static_product_4(it)\r\n 0 LOAD_CONST 3 ()\r\n 2 LOAD_FAST 0 (it)\r\n 4 LOAD_CONST 1 (4)\r\n 6 LOAD_CONST 2 (('repeat',))\r\n 8 CALL_FUNCTION_KW 2\r\n 10 RETURN_VALUE\r\n\r\nThe static version just loads the ``itertools.product`` constant, whilst\r\nthe normal version looks up a global variable and an attribute on one.\r\n\r\nEmpty set literals and ``EMPTY_SET`` are equivalent and both faster than\r\n``set()``.\r\n\r\nThey are not equivalent to ``static(set())`` which would be faster, but\r\nit would be the same static set.\r\n\r\nInstallation\r\n------------\r\n\r\nFrom `PyPI `__\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n.. code:: bash\r\n\r\n $ pip install static_variables\r\n\r\nFrom source\r\n~~~~~~~~~~~\r\n\r\n.. code:: bash\r\n\r\n $ git clone 'https://github.com/MitalAshok/static_variables.git'\r\n $ python ./static_variables/setup.py install\r\n\r\nHow does it work?\r\n-----------------\r\n\r\n``static_variables``\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nThis creates a new variable in the closure of a function. The closure\r\nremains between function calls.\r\n\r\nIt replaces ``(LOAD|STORE|DELETE)_GLOBAL`` and\r\n``(LOAD|STORE|DELETE)_FAST`` (local variables) opcodes in the bytecode\r\nwith ``(LOAD|STORE|DELETE)_DEREF`` (load from the closure) ones.\r\n\r\n``static``\r\n~~~~~~~~~~\r\n\r\nThe bytecode in Python is stack-based. ``resolve_static`` looks for a\r\n``LOAD_GLOBAL 'static'`` opcode and then starts tracking what the size\r\nof the stack will be. When the stack size reaches ``0`` and a\r\n``CALL_FUNCTION 1`` (call the top of the stack with 1 item from below it\r\non the stack) opcode is reached, it extracts the bytecode, creates a new\r\nfunction, and calls it to evaluate the bytecode. The whole\r\n``static(...)`` is replaced with ``LOAD_CONST``, to load a constant\r\nvalue which is appended to the code's ``co_consts``.\r\n\r\n``empty_set_literal``\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nWhile iterating over the bytecode, if ``BUILD_MAP 0`` is encountered\r\n(Create a new dictionary from the previous 0 items. i.e., an empty\r\ndictionary), it is replaced with ``BUILD_SET 0``, which creates an empty\r\nset instead. This opcode still exists even though it doesn't naturally\r\noccur so that it's argument still correlates with the number of items to\r\npop off of the stack to build the set with.\r\n\r\nIf a ``LOAD_GLOBAL 'EMPTY_SET'`` is encountered, it is always replaced\r\nwith a ``BUILD_SET 0`` (i.e., a new empty set.)\r\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/MitalAshok/static_variables", "keywords": "library", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "static_variables", "package_url": "https://pypi.org/project/static_variables/", "platform": "any", "project_url": "https://pypi.org/project/static_variables/", "project_urls": { "Homepage": "https://github.com/MitalAshok/static_variables" }, "release_url": "https://pypi.org/project/static_variables/0.0.5/", "requires_dist": null, "requires_python": "", "summary": "Static variables for Python", "version": "0.0.5" }, "last_serial": 3138351, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "b9ad71dfccce06df21a870202147cdb7", "sha256": "18d7ff30d44ca6cc4864be5b0d4a7eb8eca1f921261f637ffcde3238ee6397ee" }, "downloads": -1, "filename": "static_variables-0.0.1.tar.gz", "has_sig": false, "md5_digest": "b9ad71dfccce06df21a870202147cdb7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9173, "upload_time": "2017-08-09T23:06:16", "url": "https://files.pythonhosted.org/packages/16/84/6bff187338157ec0487a57194b8a2a94d363c6eb24128da59d00ecd6b4de/static_variables-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "65f5f8213548ad9ee308f8a183cb7755", "sha256": "f4eb62628501a5eeeb06f7438a15b1fa414b950b5c7562c3144e3ec9ccafd802" }, "downloads": -1, "filename": "static_variables-0.0.2.tar.gz", "has_sig": false, "md5_digest": "65f5f8213548ad9ee308f8a183cb7755", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10074, "upload_time": "2017-08-11T18:48:15", "url": "https://files.pythonhosted.org/packages/17/f6/84bb2785363742b90a6b71c9f7a801e9e3078238ea45ec9392900a766fe1/static_variables-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "af7571c09d7354e35c8e7d200dd5e0b9", "sha256": "983ea06ed889af3302c8ee3a4ed81401b89b211c6419377d659af753f6b10f8a" }, "downloads": -1, "filename": "static_variables-0.0.3.tar.gz", "has_sig": false, "md5_digest": "af7571c09d7354e35c8e7d200dd5e0b9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13217, "upload_time": "2017-08-13T13:26:46", "url": "https://files.pythonhosted.org/packages/1f/c1/a174eb1c01dd445c3d4881f511d36ba3432c54ecae88fd9498025a4fcc57/static_variables-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "64bc6f681d1acec90de6d6ec5bc8693c", "sha256": "1a0ca80c053a1b2c4ec985395e8fc1ac0e77171acd3ac7b4a141ecd294cde363" }, "downloads": -1, "filename": "static_variables-0.0.4.tar.gz", "has_sig": false, "md5_digest": "64bc6f681d1acec90de6d6ec5bc8693c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13289, "upload_time": "2017-08-13T22:37:06", "url": "https://files.pythonhosted.org/packages/42/eb/5c27a17d3a8a9f426bdc54795eb9cdaddebfbbc375e58761026b7aaffaf9/static_variables-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "a2d53d761ccd43d8ed3050f86fa6b521", "sha256": "1c714529689a7db1d7ea3713587dc18f5aed1980b9dded0cbadd67af9c0ce562" }, "downloads": -1, "filename": "static_variables-0.0.5.tar.gz", "has_sig": false, "md5_digest": "a2d53d761ccd43d8ed3050f86fa6b521", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13392, "upload_time": "2017-08-31T18:42:12", "url": "https://files.pythonhosted.org/packages/af/97/40a22e07e1136efcd04fdc1f26589e0429c92f8097f196d3d17bc75cb3cb/static_variables-0.0.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a2d53d761ccd43d8ed3050f86fa6b521", "sha256": "1c714529689a7db1d7ea3713587dc18f5aed1980b9dded0cbadd67af9c0ce562" }, "downloads": -1, "filename": "static_variables-0.0.5.tar.gz", "has_sig": false, "md5_digest": "a2d53d761ccd43d8ed3050f86fa6b521", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13392, "upload_time": "2017-08-31T18:42:12", "url": "https://files.pythonhosted.org/packages/af/97/40a22e07e1136efcd04fdc1f26589e0429c92f8097f196d3d17bc75cb3cb/static_variables-0.0.5.tar.gz" } ] }