forked from cool-RR/python_toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpackage_finder.py
More file actions
118 lines (83 loc) · 3.37 KB
/
package_finder.py
File metadata and controls
118 lines (83 loc) · 3.37 KB
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
# Copyright 2009-2014 Ram Rachum.
# This program is distributed under the MIT license.
'''
Defines functions related to finding Python packages.
See documentation of `get_module_names` for more info.
This module is hacky.
'''
import glob
import os
import types
import pkgutil
import pathlib
from python_toolbox import dict_tools
_extensions_by_priority = ['.pyo', '.pyc', '.pyw', '.py']
'''List of possible extenstions of Python modules, ordered by priority.'''
def get_module_names(root_path):
'''
Find names of all modules in a path.
Supports zip-imported modules.
'''
assert isinstance(root_path, basestring)
result = []
for _, module_name, _ in pkgutil.iter_modules([root_path]):
result.append('.' + module_name)
return result
def get_packages_and_modules_filenames(root, recursive=False):
'''
Find the filenames of all of the packages and modules inside the package.
`root` may be a module, package, or a path.
todo: module? really?
todo: needs testing
'''
if isinstance(root, types.ModuleType):
root_module = root
root_path = pathlib.Path(root_module).parent
elif isinstance(root, (str, pathlib.PurePath)):
root_path = pathlib.Path(root).absolute()
# Not making `root_module`, it might not be imported.
######################################################
result = []
for entry in os.listdir(root_path):
full_path = root_path / entry
if is_module(full_path):
result.append(entry)
continue
elif is_package(full_path):
result.append(entry)
if recursive:
inner_results = get_packages_and_modules_filenames(
full_path,
recursive=True
)
result += [entry / thing for thing in inner_results]
### Filtering out duplicate filenames for the same module: ################
# #
filename_to_module_name = {
filename: filename.stem for filename in result
}
module_name_to_filenames = \
dict_tools.reverse_with_set_values(filename_to_module_name)
for module_name, filenames in module_name_to_filenames.iteritems():
if len(filenames) <= 1:
# Does this save us from the case of packages?
continue
filenames_by_priority = sorted(
filenames,
key=lambda filename:
_extensions_by_priority.index(filename.suffix),
)
redundant_filenames = filenames_by_priority[1:]
for redundant_filename in redundant_filenames:
result.remove(redundant_filename)
# #
### Done filtering duplicate filenames for the same module. ###############
return [root_path / entry for entry in result]
def is_package(path):
'''Is the given path a Python package?'''
path = pathlib.Path(path)
return path.is_dir() and list(path.glob('__init__.*'))
def is_module(path):
'''Is the given path a Python single-file module?'''
path = pathlib.Path(path)
return path.suffix.lower() in ['.py', '.pyc', '.pyo', '.pyw', '.pyd']