File: __init__.py

package info (click to toggle)
python-passlib 1.9.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,184 kB
  • sloc: python: 26,132; makefile: 7
file content (141 lines) | stat: -rw-r--r-- 3,901 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"""passlib.utils.compat - python 2/3 compatibility helpers"""

import logging
import sys
from contextlib import nullcontext
from types import ModuleType
from typing import Callable


def add_doc(obj: object, doc: str) -> None:
    """add docstring to an object"""
    obj.__doc__ = doc


__all__ = [
    # type detection
    ##    'is_mapping',
    "numeric_types",
    "unicode_or_bytes",
    # unicode/bytes types & helpers
    "bascii_to_str",
    "str_to_bascii",
    "join_unicode",
    "join_bytes",
    # context helpers
    "nullcontext",
    # introspection
    "get_method_function",
    "add_doc",
]

# begin accumulating mapping of lazy-loaded attrs,
# 'merged' into module at bottom
_lazy_attrs: dict[str, object] = dict()


#: alias for isinstance() tests to detect any string type
unicode_or_bytes = (str, bytes)

join_unicode = "".join
join_bytes = b"".join


def bascii_to_str(s):
    assert isinstance(s, bytes)
    return s.decode("ascii")


def str_to_bascii(s):
    assert isinstance(s, str)
    return s.encode("ascii")


def iter_byte_chars(s):
    assert isinstance(s, bytes)
    # FIXME: there has to be a better way to do this
    return (bytes([c]) for c in s)


# TODO: move docstrings to funcs...
add_doc(bascii_to_str, "helper to convert ascii bytes -> native str")
add_doc(str_to_bascii, "helper to convert ascii native str -> bytes")

# byte_elem_value -- function to convert byte element to integer -- a noop under PY3

add_doc(iter_byte_chars, "iterate over byte string as sequence of 1-byte strings")

numeric_types = (int, float)


def get_method_function(func: Callable) -> Callable:
    """given (potential) method, return underlying function"""
    return getattr(func, "__func__", func)


def _import_object(source):
    """helper to import object from module; accept format `path.to.object`"""
    modname, modattr = source.rsplit(".", 1)
    mod = __import__(modname, fromlist=[modattr], level=0)
    return getattr(mod, modattr)


class _LazyOverlayModule(ModuleType):
    """proxy module which overlays original module,
    and lazily imports specified attributes.

    this is mainly used to prevent importing of resources
    that are only needed by certain password hashes,
    yet allow them to be imported from a single location.

    used by :mod:`passlib.utils`, :mod:`passlib.crypto`,
    and :mod:`passlib.utils.compat`.
    """

    @classmethod
    def replace_module(cls, name, attrmap):
        orig = sys.modules[name]
        self = cls(name, attrmap, orig)
        sys.modules[name] = self
        return self

    def __init__(self, name, attrmap, proxy=None):
        ModuleType.__init__(self, name)
        self.__attrmap = attrmap
        self.__proxy = proxy
        self.__log = logging.getLogger(name)

    def __getattr__(self, attr):
        proxy = self.__proxy
        if proxy and hasattr(proxy, attr):
            return getattr(proxy, attr)
        attrmap = self.__attrmap
        if attr in attrmap:
            source = attrmap[attr]
            if callable(source):  # noqa: SIM108
                value = source()
            else:
                value = _import_object(source)
            setattr(self, attr, value)
            self.__log.debug("loaded lazy attr %r: %r", attr, value)
            return value
        raise AttributeError(f"'module' object has no attribute '{attr}'")

    def __repr__(self):
        proxy = self.__proxy
        if proxy:
            return repr(proxy)
        return ModuleType.__repr__(self)

    def __dir__(self):
        attrs = set(dir(self.__class__))
        attrs.update(self.__dict__)
        attrs.update(self.__attrmap)
        proxy = self.__proxy
        if proxy is not None:
            attrs.update(dir(proxy))
        return list(attrs)


# replace this module with overlay that will lazily import attributes.
_LazyOverlayModule.replace_module(__name__, _lazy_attrs)