Description
===========
This package intends to offer a mechanism to add properties to your modules.
The primary use-case of this functionality is to allow for the deferred
execution of expensive-to-compute module/package level globals without needing
to explicitly call a function.
Generally speaking, this module has two APIs that offer this functionality.
You can use either of them or both of them, and everything will work more or
less as you expect it to, as long as you follow the rules.
Note that this package supports the use of basically any descriptor at the
module-level, not just properties.
Usage
=====
The first method of using the package supports basically any descriptor being
used as a decorator in the standard way. We show the use of ``property`` for the
sake of brevity::
@property
def module_property(mod):
'''mod is the module that this property was defined in'''
import mprop; mprop.init()
.. warning: If you use properties or any of your own descriptors, you must
call ``mprop.init()`` after defining all of your properties/descriptors.
You *can* call ``mprop.init()`` multiple times if you need to add more
properties/descriptors during runtime.
Because remembering to put the import/init call at the bottom of a module can
be annoying, we've got a special decorator that works just like the property
object, but handles the ``mprop.init()`` call for you::
from mprop import mproperty
@mproperty
def module_property2(mod):
'''I work exactly the same as the earlier module property, but you
don't need to make a subsequent call to ``mprop.init()``'''
Regardless of which method you use, if the name of your module is ``mod`` those
that import the module can access the properties as normal::
# example.py
from mprop import mproperty
@mproperty
def prop(mod):
return "I was called!"
def fcn():
# I can access the property via:
print _pmodule.prop
# test.py
import example
# the below should print "I was called!"
print example.prop
# this should also print "I was called!"
example.fcn()
Referencing properties from within the module
=============================================
After initialization, your code may want to reference the global properties.
If you try to access the properties directly, you will get a NameError unless
you locally aliased the value, the initialization has not completed, or unless
someone else injected a value with that name into the globals.
If you would like to directly access properties in the module, from within the
module, you must reference them relative to the newly generated module. This is
available from within your functions defined in the module via ``_pmodule``,
which is the "property-enhanced module" that offers property access.
If you find it necessary to require access to the original module object
(which doesn't support properties), you can access ``_module`` from the global
namspace.
How it works
============
The short version: we replace the standard Python module instance in
``sys.modules`` during module import, which allows us to ensure that the module
is replaced everywhere it is used. We perform some magic to ensure that
everything available in the original module is available in the replacement
module (the replacement module's ``__dict__`` is the module's globals), and we
post-process everything in the module's global namespace to pull out
descriptors as necessary.
As of February 1, 2019, in order to offer proper ``@mproperty.setter/deleter``
support, we've started initializing the module after import via the system
profiler. More specifically, any time we get a call for ``@mproperty``, we see
if we've installed a system profiler to clean up after this module. In CPython
via the ``sys.setprofile()`` handler, we look for return calls from module
creation. These auto-nest, and auto-remove themselves, so by the time your final
mprop-using module is done initializing, our profilers are removed.
We used to inject a special ``__getattribute__`` method that auto-initializes on
first access, but it was being triggered in cases where I didn't expect.
Python magic that goes on
-------------------------
* Using ``sys.getframe()`` to pull calling frame objects to get module globals
and subsequently the module object itself
* Replace ``sys.modules`` entries
* Replace an instance ``__dict__``
* Assign descriptors during runtime by modifying the base class of the
replacement module
* Use the system profiler to discover when a module has finished loading, to
start initialization
Raw data
{
"_id": null,
"home_page": "https://github.com/josiahcarlson/mprop",
"name": "mprop",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Josiah Carlson",
"author_email": "josiah.carlson@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/41/f2/c949e0dae816dc173b1fa30ef26588bdcd94fd20478831d274a44f691a07/mprop-0.17.0.tar.gz",
"platform": null,
"description": "\nDescription\n===========\n\nThis package intends to offer a mechanism to add properties to your modules.\nThe primary use-case of this functionality is to allow for the deferred\nexecution of expensive-to-compute module/package level globals without needing\nto explicitly call a function.\n\nGenerally speaking, this module has two APIs that offer this functionality.\nYou can use either of them or both of them, and everything will work more or\nless as you expect it to, as long as you follow the rules.\n\nNote that this package supports the use of basically any descriptor at the\nmodule-level, not just properties.\n\nUsage\n=====\n\nThe first method of using the package supports basically any descriptor being\nused as a decorator in the standard way. We show the use of ``property`` for the\nsake of brevity::\n\n @property\n def module_property(mod):\n '''mod is the module that this property was defined in'''\n\n import mprop; mprop.init()\n\n.. warning: If you use properties or any of your own descriptors, you must\n call ``mprop.init()`` after defining all of your properties/descriptors.\n You *can* call ``mprop.init()`` multiple times if you need to add more\n properties/descriptors during runtime.\n\nBecause remembering to put the import/init call at the bottom of a module can\nbe annoying, we've got a special decorator that works just like the property\nobject, but handles the ``mprop.init()`` call for you::\n\n from mprop import mproperty\n\n @mproperty\n def module_property2(mod):\n '''I work exactly the same as the earlier module property, but you\n don't need to make a subsequent call to ``mprop.init()``'''\n\nRegardless of which method you use, if the name of your module is ``mod`` those\nthat import the module can access the properties as normal::\n\n # example.py\n from mprop import mproperty\n\n @mproperty\n def prop(mod):\n return \"I was called!\"\n\n def fcn():\n # I can access the property via:\n print _pmodule.prop\n\n # test.py\n import example\n\n # the below should print \"I was called!\"\n print example.prop\n # this should also print \"I was called!\"\n example.fcn()\n\nReferencing properties from within the module\n=============================================\n\nAfter initialization, your code may want to reference the global properties.\nIf you try to access the properties directly, you will get a NameError unless\nyou locally aliased the value, the initialization has not completed, or unless\nsomeone else injected a value with that name into the globals.\n\nIf you would like to directly access properties in the module, from within the\nmodule, you must reference them relative to the newly generated module. This is\navailable from within your functions defined in the module via ``_pmodule``,\nwhich is the \"property-enhanced module\" that offers property access.\n\nIf you find it necessary to require access to the original module object\n(which doesn't support properties), you can access ``_module`` from the global\nnamspace.\n\nHow it works\n============\n\nThe short version: we replace the standard Python module instance in\n``sys.modules`` during module import, which allows us to ensure that the module\nis replaced everywhere it is used. We perform some magic to ensure that\neverything available in the original module is available in the replacement\nmodule (the replacement module's ``__dict__`` is the module's globals), and we\npost-process everything in the module's global namespace to pull out\ndescriptors as necessary.\n\nAs of February 1, 2019, in order to offer proper ``@mproperty.setter/deleter``\nsupport, we've started initializing the module after import via the system\nprofiler. More specifically, any time we get a call for ``@mproperty``, we see\nif we've installed a system profiler to clean up after this module. In CPython\nvia the ``sys.setprofile()`` handler, we look for return calls from module\ncreation. These auto-nest, and auto-remove themselves, so by the time your final\nmprop-using module is done initializing, our profilers are removed.\n\nWe used to inject a special ``__getattribute__`` method that auto-initializes on\nfirst access, but it was being triggered in cases where I didn't expect.\n\nPython magic that goes on\n-------------------------\n\n* Using ``sys.getframe()`` to pull calling frame objects to get module globals\n and subsequently the module object itself\n* Replace ``sys.modules`` entries\n* Replace an instance ``__dict__``\n* Assign descriptors during runtime by modifying the base class of the\n replacement module\n* Use the system profiler to discover when a module has finished loading, to\n start initialization\n\n\n",
"bugtrack_url": null,
"license": "GNU LGPL v2.1",
"summary": "Module properties for Python",
"version": "0.17.0",
"project_urls": {
"Homepage": "https://github.com/josiahcarlson/mprop"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "41f2c949e0dae816dc173b1fa30ef26588bdcd94fd20478831d274a44f691a07",
"md5": "89201c1be95e3a06d5242d801409cc31",
"sha256": "e14597c821481ee569aa80ec79a71345e6a627cb8b1e3e2038450fe2128eabd2"
},
"downloads": -1,
"filename": "mprop-0.17.0.tar.gz",
"has_sig": false,
"md5_digest": "89201c1be95e3a06d5242d801409cc31",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14343,
"upload_time": "2024-10-16T20:57:17",
"upload_time_iso_8601": "2024-10-16T20:57:17.323675Z",
"url": "https://files.pythonhosted.org/packages/41/f2/c949e0dae816dc173b1fa30ef26588bdcd94fd20478831d274a44f691a07/mprop-0.17.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-16 20:57:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "josiahcarlson",
"github_project": "mprop",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "mprop"
}