55
66from typing import Any
77from typing import Dict
8+ from typing import Generator
89from typing import List
910from typing import Optional
1011from typing import Union
@@ -27,6 +28,11 @@ class Factory(object):
2728 Factory class to create various elements needed by Poetry.
2829 """
2930
31+ DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP = {
32+ "allows_prereleases" : "allow-prereleases" ,
33+ "develop" : "editable" ,
34+ }
35+
3036 def create_poetry (
3137 self , cwd = None , with_dev = True
3238 ): # type: (Optional[Path]. bool) -> Poetry
@@ -182,49 +188,40 @@ def create_dependency(
182188 constraint = "*"
183189
184190 if isinstance (constraint , dict ):
185- optional = constraint .get ("optional" , False )
186- python_versions = constraint .get ("python" )
187- platform = constraint .get ("platform" )
188- markers = constraint .get ("markers" )
189- if "allows-prereleases" in constraint :
190- message = (
191- 'The "{}" dependency specifies '
192- 'the "allows-prereleases" property, which is deprecated. '
193- 'Use "allow-prereleases" instead.' .format (name )
194- )
195- warn (message , DeprecationWarning )
196- logger .warning (message )
197-
198- allows_prereleases = constraint .get (
199- "allow-prereleases" , constraint .get ("allows-prereleases" , False )
191+ constraint_without_deprecated_keys = cls .as_constraint_with_deprecated_keys_renamed_to_current_keys (
192+ dependency_name = name , constraint = constraint ,
200193 )
194+ optional = constraint_without_deprecated_keys .get ("optional" , False )
195+ python_versions = constraint_without_deprecated_keys .get ("python" )
196+ platform = constraint_without_deprecated_keys .get ("platform" )
197+ markers = constraint_without_deprecated_keys .get ("markers" )
201198
202- if "git" in constraint :
199+ if "git" in constraint_without_deprecated_keys :
203200 # VCS dependency
204201 dependency = VCSDependency (
205202 name ,
206203 "git" ,
207- constraint ["git" ],
208- branch = constraint .get ("branch" , None ),
209- tag = constraint .get ("tag" , None ),
210- rev = constraint .get ("rev" , None ),
204+ constraint_without_deprecated_keys ["git" ],
205+ branch = constraint_without_deprecated_keys .get ("branch" , None ),
206+ tag = constraint_without_deprecated_keys .get ("tag" , None ),
207+ rev = constraint_without_deprecated_keys .get ("rev" , None ),
211208 category = category ,
212209 optional = optional ,
213- develop = constraint .get ("develop " , False ),
214- extras = constraint .get ("extras" , []),
210+ editable = constraint_without_deprecated_keys .get ("editable " , False ),
211+ extras = constraint_without_deprecated_keys .get ("extras" , []),
215212 )
216- elif "file" in constraint :
217- file_path = Path (constraint ["file" ])
213+ elif "file" in constraint_without_deprecated_keys :
214+ file_path = Path (constraint_without_deprecated_keys ["file" ])
218215
219216 dependency = FileDependency (
220217 name ,
221218 file_path ,
222219 category = category ,
223220 base = root_dir ,
224- extras = constraint .get ("extras" , []),
221+ extras = constraint_without_deprecated_keys .get ("extras" , []),
225222 )
226- elif "path" in constraint :
227- path = Path (constraint ["path" ])
223+ elif "path" in constraint_without_deprecated_keys :
224+ path = Path (constraint_without_deprecated_keys ["path" ])
228225
229226 if root_dir :
230227 is_file = root_dir .joinpath (path ).is_file ()
@@ -238,7 +235,7 @@ def create_dependency(
238235 category = category ,
239236 optional = optional ,
240237 base = root_dir ,
241- extras = constraint .get ("extras" , []),
238+ extras = constraint_without_deprecated_keys .get ("extras" , []),
242239 )
243240 else :
244241 dependency = DirectoryDependency (
@@ -247,27 +244,31 @@ def create_dependency(
247244 category = category ,
248245 optional = optional ,
249246 base = root_dir ,
250- develop = constraint .get ("develop" , False ),
251- extras = constraint .get ("extras" , []),
247+ editable = constraint_without_deprecated_keys .get (
248+ "editable" , False
249+ ),
250+ extras = constraint_without_deprecated_keys .get ("extras" , []),
252251 )
253- elif "url" in constraint :
252+ elif "url" in constraint_without_deprecated_keys :
254253 dependency = URLDependency (
255254 name ,
256- constraint ["url" ],
255+ constraint_without_deprecated_keys ["url" ],
257256 category = category ,
258257 optional = optional ,
259- extras = constraint .get ("extras" , []),
258+ extras = constraint_without_deprecated_keys .get ("extras" , []),
260259 )
261260 else :
262- version = constraint ["version" ]
261+ version = constraint_without_deprecated_keys ["version" ]
263262
264263 dependency = Dependency (
265264 name ,
266265 version ,
267266 optional = optional ,
268267 category = category ,
269- allows_prereleases = allows_prereleases ,
270- extras = constraint .get ("extras" , []),
268+ allows_prereleases = constraint_without_deprecated_keys .get (
269+ "allow_prereleases" , False
270+ ),
271+ extras = constraint_without_deprecated_keys .get ("extras" , []),
271272 )
272273
273274 if not markers :
@@ -296,7 +297,7 @@ def create_dependency(
296297 if not marker .is_any ():
297298 dependency .marker = marker
298299
299- dependency .source_name = constraint .get ("source" )
300+ dependency .source_name = constraint_without_deprecated_keys .get ("source" )
300301 else :
301302 dependency = Dependency (name , constraint , category = category )
302303
@@ -329,12 +330,23 @@ def validate(
329330 if not isinstance (constraint , dict ):
330331 continue
331332
332- if "allows-prereleases" in constraint :
333- result ["warnings" ].append (
334- 'The "{}" dependency specifies '
335- 'the "allows-prereleases" property, which is deprecated. '
336- 'Use "allow-prereleases" instead.' .format (name )
337- )
333+ for deprecated_key in cls .deprecated_keys ():
334+ if deprecated_key in constraint :
335+ if cls .constraint_has_deprecated_key_current_key_conflict (
336+ constraint , deprecated_key
337+ ):
338+ result ["errors" ].append (
339+ cls .deprecated_constraint_key_current_key_conflict_error_message (
340+ dependency_name = name ,
341+ deprecated_key = deprecated_key ,
342+ )
343+ )
344+ else :
345+ result ["warnings" ].append (
346+ cls .constraint_key_deprecation_message (
347+ dependency_name = name , key = deprecated_key
348+ )
349+ )
338350
339351 # Checking for scripts with extras
340352 if "scripts" in config :
@@ -371,3 +383,88 @@ def locate(cls, cwd): # type: (Path) -> Path
371383 cwd
372384 )
373385 )
386+
387+ @classmethod
388+ def deprecated_keys (cls ): # type: () -> Generator[str, None, None]
389+ for key in cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP :
390+ yield key
391+
392+ @classmethod
393+ def as_constraint_with_deprecated_keys_renamed_to_current_keys (
394+ cls , dependency_name , constraint
395+ ): # type: (str, Dict[str, Any]) -> Dict[str, Any]
396+ constraint_with_renamed_keys = {}
397+ for key , value in constraint .items ():
398+ if cls .is_deprecated_constraint_key (key ):
399+ cls .raise_on_deprecated_constraint_key_current_key_conflict (
400+ dependency_name , constraint , key
401+ )
402+ cls .warn_constraint_key_is_deprecated (dependency_name , key )
403+ current_key = cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP [key ]
404+ constraint_with_renamed_keys [current_key ] = value
405+ else :
406+ constraint_with_renamed_keys [key ] = value
407+ return constraint_with_renamed_keys
408+
409+ @classmethod
410+ def raise_on_deprecated_constraint_key_current_key_conflict (
411+ cls , dependency_name , constraint , deprecated_key
412+ ): # type: (str, Dict[str, Any], str) -> None
413+ """Raise `RuntimeError` when both a deprecated key and it's current, updated counterpart (key) are contained in constraint."""
414+ if cls .constraint_has_deprecated_key_current_key_conflict (
415+ constraint , deprecated_key
416+ ):
417+ raise RuntimeError (
418+ cls .deprecated_constraint_key_current_key_conflict_error_message (
419+ dependency_name , deprecated_key ,
420+ )
421+ )
422+
423+ @classmethod
424+ def constraint_has_deprecated_key_current_key_conflict (
425+ cls , constraint , deprecated_key
426+ ): # type: (Dict[str, Any], str) -> bool
427+ current_key = cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP [deprecated_key ]
428+ return current_key in constraint
429+
430+ @classmethod
431+ def deprecated_constraint_key_current_key_conflict_error_message (
432+ cls , dependency_name , deprecated_key
433+ ): # type: (str, str) -> str
434+ current_key = cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP [deprecated_key ]
435+ return (
436+ 'The "{dependency_name}" dependency specifies '
437+ 'both the "{current_key}" property and the deprecated "{deprecated_key}" property. '
438+ 'Please remove "{deprecated_key}" and resolve value conflicts!' .format (
439+ dependency_name = dependency_name ,
440+ current_key = current_key ,
441+ deprecated_key = deprecated_key ,
442+ )
443+ )
444+
445+ @classmethod
446+ def is_deprecated_constraint_key (cls , key ): # type: (str) -> bool
447+ return key in cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP
448+
449+ @classmethod
450+ def warn_constraint_key_is_deprecated (
451+ cls , dependency_name , key
452+ ): # type: (str, str) -> None
453+ message = cls .constraint_key_deprecation_message (dependency_name , key )
454+ warn (message , DeprecationWarning )
455+ logging .warning (message )
456+
457+ @classmethod
458+ def constraint_key_deprecation_message (
459+ cls , dependency_name , key
460+ ): # type: (str, str) -> str
461+ current_key = cls .DEPRECATED_CONSTRAINT_KEY_CURRENT_KEY_MAP [key ]
462+ return (
463+ 'The "{dependency_name}" dependency specifies '
464+ 'the "{deprecated_key}" property, which is deprecated. '
465+ 'Use "{current_key}" instead.' .format (
466+ dependency_name = dependency_name ,
467+ deprecated_key = key ,
468+ current_key = current_key ,
469+ )
470+ )
0 commit comments