12
12
13
13
from ._version import __version__ # NOQA
14
14
15
- ASYNC_TO_SYNC = {
15
+ __all__ = [
16
+ "Rule" ,
17
+ "cmdclass_build_py" ,
18
+ ]
19
+
20
+
21
+ _ASYNC_TO_SYNC = {
16
22
"__aenter__" : "__enter__" ,
17
23
"__aexit__" : "__exit__" ,
18
24
"__aiter__" : "__iter__" ,
27
33
"StopAsyncIteration" : "StopIteration" ,
28
34
}
29
35
36
+
37
+ class Rule :
38
+ """A single set of rules for 'unasync'ing file(s)"""
39
+
40
+ def __init__ (self , fromdir , todir , replacements = None ):
41
+ self .fromdir = fromdir .replace ("/" , os .sep )
42
+ self .todir = todir .replace ("/" , os .sep )
43
+
44
+ # Add any additional user-defined token replacements to our list.
45
+ self .token_replacements = _ASYNC_TO_SYNC .copy ()
46
+ for key , val in (replacements or {}).items ():
47
+ self .token_replacements [key ] = val
48
+
49
+ def match (self , filepath ):
50
+ """Determines if a Rule matches a given filepath and if so
51
+ returns a higher comparable value if the match is more specific.
52
+ """
53
+ file_segments = [x for x in filepath .split (os .sep ) if x ]
54
+ from_segments = [x for x in self .fromdir .split (os .sep ) if x ]
55
+ len_from_segments = len (from_segments )
56
+
57
+ if len_from_segments > len (file_segments ):
58
+ return False
59
+
60
+ for i in range (len (file_segments ) - len_from_segments + 1 ):
61
+ if file_segments [i : i + len_from_segments ] == from_segments :
62
+ return len_from_segments , i
63
+
64
+ return False
65
+
66
+ def unasync_file (self , filepath ):
67
+ with open (filepath , "rb" ) as f :
68
+ write_kwargs = {}
69
+ if sys .version_info [0 ] >= 3 :
70
+ encoding , _ = std_tokenize .detect_encoding (f .readline )
71
+ write_kwargs ["encoding" ] = encoding
72
+ f .seek (0 )
73
+ tokens = tokenize (f )
74
+ tokens = self .unasync_tokens (tokens )
75
+ result = untokenize (tokens )
76
+ outfilepath = filepath .replace (self .fromdir , self .todir )
77
+ makedirs_existok (os .path .dirname (outfilepath ))
78
+ with open (outfilepath , "w" , ** write_kwargs ) as f :
79
+ print (result , file = f , end = "" )
80
+
81
+ def unasync_tokens (self , tokens ):
82
+ # TODO __await__, ...?
83
+ used_space = None
84
+ for space , toknum , tokval in tokens :
85
+ if tokval in ["async" , "await" ]:
86
+ # When removing async or await, we want to use the whitespace that
87
+ # was before async/await before the next token so that
88
+ # `print(await stuff)` becomes `print(stuff)` and not
89
+ # `print( stuff)`
90
+ used_space = space
91
+ else :
92
+ if toknum == std_tokenize .NAME :
93
+ tokval = self .unasync_name (tokval )
94
+ elif toknum == std_tokenize .STRING :
95
+ left_quote , name , right_quote = tokval [0 ], tokval [1 :- 1 ], tokval [- 1 ]
96
+ tokval = left_quote + self .unasync_name (name ) + right_quote
97
+ if used_space is None :
98
+ used_space = space
99
+ yield (used_space , tokval )
100
+ used_space = None
101
+
102
+ def unasync_name (self , name ):
103
+ if name in self .token_replacements :
104
+ return self .token_replacements [name ]
105
+ # Convert classes prefixed with 'Async' into 'Sync'
106
+ elif len (name ) > 5 and name .startswith ("Async" ) and name [5 ].isupper ():
107
+ return "Sync" + name [5 :]
108
+ return name
109
+
110
+
30
111
Token = collections .namedtuple ("Token" , ["type" , "string" , "start" , "end" , "line" ])
31
112
32
113
@@ -60,37 +141,6 @@ def tokenize(f):
60
141
last_end = (tok .end [0 ] + 1 , 0 )
61
142
62
143
63
- def unasync_name (name ):
64
- if name in ASYNC_TO_SYNC :
65
- return ASYNC_TO_SYNC [name ]
66
- # Convert classes prefixed with 'Async' into 'Sync'
67
- elif len (name ) > 5 and name .startswith ("Async" ) and name [5 ].isupper ():
68
- return "Sync" + name [5 :]
69
- return name
70
-
71
-
72
- def unasync_tokens (tokens ):
73
- # TODO __await__, ...?
74
- used_space = None
75
- for space , toknum , tokval in tokens :
76
- if tokval in ["async" , "await" ]:
77
- # When removing async or await, we want to use the whitespace that
78
- # was before async/await before the next token so that
79
- # `print(await stuff)` becomes `print(stuff)` and not
80
- # `print( stuff)`
81
- used_space = space
82
- else :
83
- if toknum == std_tokenize .NAME :
84
- tokval = unasync_name (tokval )
85
- elif toknum == std_tokenize .STRING :
86
- left_quote , name , right_quote = tokval [0 ], tokval [1 :- 1 ], tokval [- 1 ]
87
- tokval = left_quote + unasync_name (name ) + right_quote
88
- if used_space is None :
89
- used_space = space
90
- yield (used_space , tokval )
91
- used_space = None
92
-
93
-
94
144
def untokenize (tokens ):
95
145
return "" .join (space + tokval for space , tokval in tokens )
96
146
@@ -103,34 +153,21 @@ def makedirs_existok(dir):
103
153
raise
104
154
105
155
106
- def unasync_file (filepath , fromdir , todir ):
107
- with open (filepath , "rb" ) as f :
108
- write_kwargs = {}
109
- if sys .version_info [0 ] >= 3 :
110
- encoding , _ = std_tokenize .detect_encoding (f .readline )
111
- write_kwargs ["encoding" ] = encoding
112
- f .seek (0 )
113
- tokens = tokenize (f )
114
- tokens = unasync_tokens (tokens )
115
- result = untokenize (tokens )
116
- outfilepath = filepath .replace (fromdir , todir )
117
- makedirs_existok (os .path .dirname (outfilepath ))
118
- with open (outfilepath , "w" , ** write_kwargs ) as f :
119
- print (result , file = f , end = "" )
156
+ _DEFAULT_RULE = Rule (fromdir = "/_async/" , todir = "/_sync/" )
120
157
121
158
122
- class build_py (orig .build_py ):
159
+ class _build_py (orig .build_py ):
123
160
"""
124
161
Subclass build_py from setuptools to modify its behavior.
125
162
126
163
Convert files in _async dir from being asynchronous to synchronous
127
164
and saves them in _sync dir.
128
165
"""
129
166
130
- RENAME_DIR_FROM_TO = ("_async" , "_sync" ) # Controls what directory will be renamed.
167
+ UNASYNC_RULES = (_DEFAULT_RULE ,)
131
168
132
169
def run (self ):
133
- dir_from , dir_to = self .RENAME_DIR_FROM_TO
170
+ rules = self .UNASYNC_RULES
134
171
135
172
self ._updated_files = []
136
173
@@ -143,8 +180,17 @@ def run(self):
143
180
144
181
# Our modification!
145
182
for f in self ._updated_files :
146
- if os .sep + dir_from + os .sep in f :
147
- unasync_file (f , dir_from , dir_to )
183
+ found_rule = None
184
+ found_weight = None
185
+
186
+ for rule in rules :
187
+ weight = rule .match (f )
188
+ if weight and (found_weight is None or weight > found_weight ):
189
+ found_rule = rule
190
+ found_weight = weight
191
+
192
+ if found_rule :
193
+ found_rule .unasync_file (f )
148
194
149
195
# Remaining base class code
150
196
self .byte_compile (self .get_outputs (include_bytecode = 0 ))
@@ -156,8 +202,10 @@ def build_module(self, module, module_file, package):
156
202
return outfile , copied
157
203
158
204
159
- def customize_build_py (rename_dir_from_to = ("_async" , "_sync" )):
160
- class _build_py (build_py ):
161
- RENAME_DIR_FROM_TO = rename_dir_from_to
205
+ def cmdclass_build_py (rules = (_DEFAULT_RULE ,)):
206
+ """Creates a 'build_py' class for use within 'cmdclass={"build_py": ...}'"""
207
+
208
+ class _custom_build_py (_build_py ):
209
+ UNASYNC_RULES = rules
162
210
163
- return _build_py
211
+ return _custom_build_py
0 commit comments