source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/custom_import.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 6.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""
4| This file is part of the web2py Web Framework
5| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
6| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
7
8Support for smart import syntax for web2py applications
9-------------------------------------------------------
10"""
11from gluon._compat import builtin, unicodeT, to_native, reload
12import os
13import sys
14import threading
15from gluon import current
16
17NATIVE_IMPORTER = builtin.__import__
18INVALID_MODULES = set(('', 'gluon', 'applications', 'custom_import'))
19
20# backward compatibility API
21
22def custom_import_install():
23    if builtin.__import__ == NATIVE_IMPORTER:
24        INVALID_MODULES.update(sys.modules.keys())
25        builtin.__import__ = custom_importer
26
27
28def track_changes(track=True):
29    assert track in (True, False), "must be True or False"
30    current.request._custom_import_track_changes = track
31
32
33def is_tracking_changes():
34    return current.request._custom_import_track_changes
35
36
37# see https://docs.python.org/3/library/functions.html#__import__
38# Changed in Python 3.3: Negative values for level are no longer supported,
39# which also changes the default value to 0 (was -1)
40_DEFAULT_LEVEL = 0 if sys.version_info[:2] >= (3, 3) else -1
41
42def custom_importer(name, globals={}, locals=None, fromlist=(), level=_DEFAULT_LEVEL):
43    """
44    web2py's custom importer. It behaves like the standard Python importer but
45    it tries to transform import statements as something like
46    "import applications.app_name.modules.x".
47    If the import fails, it falls back on builtin importer.
48    """
49
50    # support for non-ascii name
51    if isinstance(name, unicodeT):
52        name = to_native(name)
53
54    if hasattr(current, 'request') \
55            and level <= 0 \
56            and name.partition('.')[0] not in INVALID_MODULES:
57        # absolute import from application code
58        try:
59            return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
60        except (ImportError, KeyError):
61            pass
62        if current.request._custom_import_track_changes:
63            base_importer = TRACK_IMPORTER
64        else:
65            base_importer = NATIVE_IMPORTER
66        # rstrip for backward compatibility
67        items = current.request.folder.rstrip(os.sep).split(os.sep)
68        modules_prefix = '.'.join(items[-2:]) + '.modules'
69        if not fromlist:
70            # "import x" or "import x.y"
71            result = None
72            for itemname in name.split("."):
73                new_mod = base_importer(
74                    modules_prefix, globals, locals, (itemname,), level)
75                modules_prefix += "." + itemname
76                if result is None:
77                    try:
78                        result = sys.modules[modules_prefix]
79                    except KeyError:
80                        raise ImportError("No module named %s" % modules_prefix)
81            return result
82        else:
83            # "from x import a, b, ..."
84            pname = "%s.%s" % (modules_prefix, name)
85            return base_importer(pname, globals, locals, fromlist, level)
86
87    return NATIVE_IMPORTER(name, globals, locals, fromlist, level)
88
89
90class TrackImporter(object):
91    """
92    An importer tracking the date of the module files and reloading them when
93    they are changed.
94    """
95
96    THREAD_LOCAL = threading.local()
97    PACKAGE_PATH_SUFFIX = os.path.sep + "__init__.py"
98
99    def __init__(self):
100        self._import_dates = {}  # Import dates of the files of the modules
101
102    def __call__(self, name, globals={}, locals=None, fromlist=(), level=_DEFAULT_LEVEL):
103        """
104        The import method itself.
105        """
106        # Check the date and reload if needed:
107        self._update_dates(name, globals, locals, fromlist, level)
108        # Try to load the module and update the dates if it works:
109        result = NATIVE_IMPORTER(name, globals, locals, fromlist, level)
110        # Module maybe loaded for the 1st time so we need to set the date
111        self._update_dates(name, globals, locals, fromlist, level)
112        return result
113
114    def _update_dates(self, name, globals, locals, fromlist, level):
115        """
116        Update all the dates associated to the statement import. A single
117        import statement may import many modules.
118        """
119        self._reload_check(name, globals, locals, level)
120        for fromlist_name in fromlist or []:
121            pname = "%s.%s" % (name, fromlist_name)
122            self._reload_check(pname, globals, locals, level)
123
124    def _reload_check(self, name, globals, locals, level):
125        """
126        Update the date associated to the module and reload the module if
127        the file changed.
128        """
129        module = sys.modules.get(name)
130        file = self._get_module_file(module)
131        if file:
132            date = self._import_dates.get(file)
133            new_date = None
134            reload_mod = False
135            mod_to_pack = False  # Module turning into a package? (special case)
136            try:
137                new_date = os.path.getmtime(file)
138            except:
139                self._import_dates.pop(file, None)  # Clean up
140                # Handle module changing in package and
141                # package changing in module:
142                if file.endswith(".py"):
143                    # Get path without file ext:
144                    file = os.path.splitext(file)[0]
145                    reload_mod = os.path.isdir(file) \
146                        and os.path.isfile(file + self.PACKAGE_PATH_SUFFIX)
147                    mod_to_pack = reload_mod
148                else:  # Package turning into module?
149                    file += ".py"
150                    reload_mod = os.path.isfile(file)
151                if reload_mod:
152                    new_date = os.path.getmtime(file)  # Refresh file date
153            if reload_mod or not date or new_date > date:
154                self._import_dates[file] = new_date
155            if reload_mod or (date and new_date > date):
156                if mod_to_pack:
157                    # Module turning into a package:
158                    mod_name = module.__name__
159                    del sys.modules[mod_name]  # Delete the module
160                    # Reload the module:
161                    NATIVE_IMPORTER(mod_name, globals, locals, [], level)
162                else:
163                    reload(module)
164
165    def _get_module_file(self, module):
166        """
167        Get the absolute path file associated to the module or None.
168        """
169        file = getattr(module, "__file__", None)
170        if file:
171            # Make path absolute if not:
172            file = os.path.splitext(file)[0] + ".py"  # Change .pyc for .py
173            if file.endswith(self.PACKAGE_PATH_SUFFIX):
174                file = os.path.dirname(file)  # Track dir for packages
175        return file
176
177TRACK_IMPORTER = TrackImporter()
Note: See TracBrowser for help on using the repository browser.