feat: add AppHandler methods, get_distribution() and get_version()
This commit is contained in:
parent
0a46dddf3f
commit
2a21e70ff1
|
@ -152,6 +152,90 @@ class AppHandler:
|
||||||
return self.config.get(f'{self.appname}.app_title',
|
return self.config.get(f'{self.appname}.app_title',
|
||||||
default=default or self.default_app_title)
|
default=default or self.default_app_title)
|
||||||
|
|
||||||
|
def get_distribution(self, obj=None):
|
||||||
|
"""
|
||||||
|
Returns the appropriate Python distribution name.
|
||||||
|
|
||||||
|
If ``obj`` is specified, this will attempt to locate the
|
||||||
|
distribution based on the top-level module which contains the
|
||||||
|
object's type/class.
|
||||||
|
|
||||||
|
If ``obj`` is *not* specified, this behaves a bit differently.
|
||||||
|
It first will look for a :term:`config setting` named
|
||||||
|
``wutta.app_dist`` (or similar, dpending on :attr:`appname`).
|
||||||
|
If there is such a config value, it is returned. Otherwise
|
||||||
|
the "auto-locate" logic described above happens, but using
|
||||||
|
``self`` instead of ``obj``.
|
||||||
|
|
||||||
|
In other words by default this returns the distribution to
|
||||||
|
which the running :term:`app handler` belongs.
|
||||||
|
|
||||||
|
See also :meth:`get_version()`.
|
||||||
|
|
||||||
|
:param obj: Any object which may be used as a clue to locate
|
||||||
|
the appropriate distribution.
|
||||||
|
|
||||||
|
:returns: string, or ``None``
|
||||||
|
|
||||||
|
Also note that a *distribution* name is different from a
|
||||||
|
*package* name. The distribution name is how things appear on
|
||||||
|
PyPI for instance.
|
||||||
|
|
||||||
|
If you want to override the default distribution name (and
|
||||||
|
skip the auto-locate based on app handler) then you can define
|
||||||
|
it in config:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[wutta]
|
||||||
|
app_dist = My-Poser-Dist
|
||||||
|
"""
|
||||||
|
if obj is None:
|
||||||
|
print(self.appname)
|
||||||
|
dist = self.config.get(f'{self.appname}.app_dist')
|
||||||
|
if dist:
|
||||||
|
return dist
|
||||||
|
|
||||||
|
# TODO: do we need a config setting for app_package ?
|
||||||
|
#modpath = self.config.get(f'{self.appname}.app_package')
|
||||||
|
modpath = None
|
||||||
|
if not modpath:
|
||||||
|
modpath = type(obj if obj is not None else self).__module__
|
||||||
|
pkgname = modpath.split('.')[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
from importlib.metadata import packages_distributions
|
||||||
|
except ImportError: # python < 3.10
|
||||||
|
from importlib_metadata import packages_distributions
|
||||||
|
|
||||||
|
pkgmap = packages_distributions()
|
||||||
|
if pkgname in pkgmap:
|
||||||
|
dist = pkgmap[pkgname][0]
|
||||||
|
return dist
|
||||||
|
|
||||||
|
# fall back to configured dist, if obj lookup failed
|
||||||
|
if obj is not None:
|
||||||
|
return self.config.get(f'{self.appname}.app_dist')
|
||||||
|
|
||||||
|
def get_version(self, dist=None, obj=None):
|
||||||
|
"""
|
||||||
|
Returns the version of a given Python distribution.
|
||||||
|
|
||||||
|
If ``dist`` is not specified, calls :meth:`get_distribution()`
|
||||||
|
to get it. (It passes ``obj`` along for this).
|
||||||
|
|
||||||
|
So by default this will return the version of whichever
|
||||||
|
distribution owns the running :term:`app handler`.
|
||||||
|
|
||||||
|
:returns: Version as string.
|
||||||
|
"""
|
||||||
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
if not dist:
|
||||||
|
dist = self.get_distribution(obj=obj)
|
||||||
|
if dist:
|
||||||
|
return version(dist)
|
||||||
|
|
||||||
def get_model(self):
|
def get_model(self):
|
||||||
"""
|
"""
|
||||||
Returns the :term:`app model` module.
|
Returns the :term:`app model` module.
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import warnings
|
import warnings
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
@ -116,6 +117,88 @@ class TestAppHandler(TestCase):
|
||||||
def test_get_title(self):
|
def test_get_title(self):
|
||||||
self.assertEqual(self.app.get_title(), 'WuttJamaican')
|
self.assertEqual(self.app.get_title(), 'WuttJamaican')
|
||||||
|
|
||||||
|
def test_get_distribution(self):
|
||||||
|
|
||||||
|
# default should always be WuttJamaican (right..?)
|
||||||
|
dist = self.app.get_distribution()
|
||||||
|
self.assertEqual(dist, 'WuttJamaican')
|
||||||
|
|
||||||
|
# also works with "non-native" objects
|
||||||
|
from config import Configuration
|
||||||
|
config = Configuration({})
|
||||||
|
dist = self.app.get_distribution(config)
|
||||||
|
self.assertEqual(dist, 'python-configuration')
|
||||||
|
|
||||||
|
# can override dist via config
|
||||||
|
self.config.setdefault('wuttatest.app_dist', 'importlib_metadata')
|
||||||
|
dist = self.app.get_distribution()
|
||||||
|
self.assertEqual(dist, 'importlib_metadata')
|
||||||
|
|
||||||
|
# but the provided object takes precedence
|
||||||
|
dist = self.app.get_distribution(config)
|
||||||
|
self.assertEqual(dist, 'python-configuration')
|
||||||
|
|
||||||
|
def test_get_distribution_pre_python_3_10(self):
|
||||||
|
|
||||||
|
# the goal here is to get coverage for code which would only
|
||||||
|
# run on python 3,9 and older, but we only need that coverage
|
||||||
|
# if we are currently testing python 3.10+
|
||||||
|
if sys.version_info.major == 3 and sys.version_info.minor < 10:
|
||||||
|
pytest.skip("this test is not relevant before python 3.10")
|
||||||
|
|
||||||
|
importlib_metadata = MagicMock()
|
||||||
|
importlib_metadata.packages_distributions = MagicMock(
|
||||||
|
return_value={
|
||||||
|
'wuttjamaican': ['WuttJamaican'],
|
||||||
|
'config': ['python-configuration'],
|
||||||
|
})
|
||||||
|
|
||||||
|
orig_import = __import__
|
||||||
|
|
||||||
|
def mock_import(name, *args, **kwargs):
|
||||||
|
if name == 'importlib.metadata':
|
||||||
|
raise ImportError
|
||||||
|
if name == 'importlib_metadata':
|
||||||
|
return importlib_metadata
|
||||||
|
return orig_import(name, *args, **kwargs)
|
||||||
|
|
||||||
|
with patch('builtins.__import__', side_effect=mock_import):
|
||||||
|
|
||||||
|
# default should always be WuttJamaican (right..?)
|
||||||
|
dist = self.app.get_distribution()
|
||||||
|
self.assertEqual(dist, 'WuttJamaican')
|
||||||
|
|
||||||
|
# also works with "non-native" objects
|
||||||
|
from config import Configuration
|
||||||
|
config = Configuration({})
|
||||||
|
dist = self.app.get_distribution(config)
|
||||||
|
self.assertEqual(dist, 'python-configuration')
|
||||||
|
|
||||||
|
# hacky sort of test, just in case we can't deduce the
|
||||||
|
# package dist based on the obj - easy enough since we
|
||||||
|
# have limited the packages_distributions() above
|
||||||
|
dist = self.app.get_distribution(42)
|
||||||
|
self.assertIsNone(dist)
|
||||||
|
|
||||||
|
# can override dist via config
|
||||||
|
self.config.setdefault('wuttatest.app_dist', 'importlib_metadata')
|
||||||
|
dist = self.app.get_distribution()
|
||||||
|
self.assertEqual(dist, 'importlib_metadata')
|
||||||
|
|
||||||
|
# but the provided object takes precedence
|
||||||
|
dist = self.app.get_distribution(config)
|
||||||
|
self.assertEqual(dist, 'python-configuration')
|
||||||
|
|
||||||
|
# hacky test again, this time config override should win
|
||||||
|
dist = self.app.get_distribution(42)
|
||||||
|
self.assertEqual(dist, 'importlib_metadata')
|
||||||
|
|
||||||
|
def test_get_version(self):
|
||||||
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
# default should always be for WuttJamaican (right..?)
|
||||||
|
self.assertEqual(self.app.get_version(), version('WuttJamaican'))
|
||||||
|
|
||||||
def test_make_title(self):
|
def test_make_title(self):
|
||||||
text = self.app.make_title('foo_bar')
|
text = self.app.make_title('foo_bar')
|
||||||
self.assertEqual(text, "Foo Bar")
|
self.assertEqual(text, "Foo Bar")
|
||||||
|
|
Loading…
Reference in a new issue