]>
Commit | Line | Data |
---|---|---|
1 | import os | |
2 | from distutils import log | |
3 | import itertools | |
4 | ||
5 | ||
6 | flatten = itertools.chain.from_iterable | |
7 | ||
8 | ||
9 | class Installer: | |
10 | ||
11 | nspkg_ext = '-nspkg.pth' | |
12 | ||
13 | def install_namespaces(self): | |
14 | nsp = self._get_all_ns_packages() | |
15 | if not nsp: | |
16 | return | |
17 | filename, ext = os.path.splitext(self._get_target()) | |
18 | filename += self.nspkg_ext | |
19 | self.outputs.append(filename) | |
20 | log.info("Installing %s", filename) | |
21 | lines = map(self._gen_nspkg_line, nsp) | |
22 | ||
23 | if self.dry_run: | |
24 | # always generate the lines, even in dry run | |
25 | list(lines) | |
26 | return | |
27 | ||
28 | with open(filename, 'wt') as f: | |
29 | f.writelines(lines) | |
30 | ||
31 | def uninstall_namespaces(self): | |
32 | filename, ext = os.path.splitext(self._get_target()) | |
33 | filename += self.nspkg_ext | |
34 | if not os.path.exists(filename): | |
35 | return | |
36 | log.info("Removing %s", filename) | |
37 | os.remove(filename) | |
38 | ||
39 | def _get_target(self): | |
40 | return self.target | |
41 | ||
42 | _nspkg_tmpl = ( | |
43 | "import sys, types, os", | |
44 | "has_mfs = sys.version_info > (3, 5)", | |
45 | "p = os.path.join(%(root)s, *%(pth)r)", | |
46 | "importlib = has_mfs and __import__('importlib.util')", | |
47 | "has_mfs and __import__('importlib.machinery')", | |
48 | ( | |
49 | "m = has_mfs and " | |
50 | "sys.modules.setdefault(%(pkg)r, " | |
51 | "importlib.util.module_from_spec(" | |
52 | "importlib.machinery.PathFinder.find_spec(%(pkg)r, " | |
53 | "[os.path.dirname(p)])))" | |
54 | ), | |
55 | ( | |
56 | "m = m or " | |
57 | "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))" | |
58 | ), | |
59 | "mp = (m or []) and m.__dict__.setdefault('__path__',[])", | |
60 | "(p not in mp) and mp.append(p)", | |
61 | ) | |
62 | "lines for the namespace installer" | |
63 | ||
64 | _nspkg_tmpl_multi = ( | |
65 | 'm and setattr(sys.modules[%(parent)r], %(child)r, m)', | |
66 | ) | |
67 | "additional line(s) when a parent package is indicated" | |
68 | ||
69 | def _get_root(self): | |
70 | return "sys._getframe(1).f_locals['sitedir']" | |
71 | ||
72 | def _gen_nspkg_line(self, pkg): | |
73 | pth = tuple(pkg.split('.')) | |
74 | root = self._get_root() | |
75 | tmpl_lines = self._nspkg_tmpl | |
76 | parent, sep, child = pkg.rpartition('.') | |
77 | if parent: | |
78 | tmpl_lines += self._nspkg_tmpl_multi | |
79 | return ';'.join(tmpl_lines) % locals() + '\n' | |
80 | ||
81 | def _get_all_ns_packages(self): | |
82 | """Return sorted list of all package namespaces""" | |
83 | pkgs = self.distribution.namespace_packages or [] | |
84 | return sorted(flatten(map(self._pkg_names, pkgs))) | |
85 | ||
86 | @staticmethod | |
87 | def _pkg_names(pkg): | |
88 | """ | |
89 | Given a namespace package, yield the components of that | |
90 | package. | |
91 | ||
92 | >>> names = Installer._pkg_names('a.b.c') | |
93 | >>> set(names) == set(['a', 'a.b', 'a.b.c']) | |
94 | True | |
95 | """ | |
96 | parts = pkg.split('.') | |
97 | while parts: | |
98 | yield '.'.join(parts) | |
99 | parts.pop() | |
100 | ||
101 | ||
102 | class DevelopInstaller(Installer): | |
103 | def _get_root(self): | |
104 | return repr(str(self.egg_path)) | |
105 | ||
106 | def _get_target(self): | |
107 | return self.egg_link |