1# Copyright (C) 2008-2021 The pip developers (see AUTHORS.txt file)

2#

3# Permission is hereby granted, free of charge, to any person obtaining

4# a copy of this software and associated documentation files (the

5# "Software"), to deal in the Software without restriction, including

6# without limitation the rights to use, copy, modify, merge, publish,

7# distribute, sublicense, and/or sell copies of the Software, and to

8# permit persons to whom the Software is furnished to do so, subject to

9# the following conditions:

10# .

11# The above copyright notice and this permission notice shall be

12# included in all copies or substantial portions of the Software.

13# .

14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE

18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION

19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION

20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21

22

23 from packaging.utils import canonicalize_name

24

25# The functions below are copied from pip:

26# _internal/index/package_finder.py

27

28

29 def _find_name_version_sep(fragment: str, canonical_name: str) -> int:

30 """Find the separator's index based on the package's canonical name.

31

32 :param fragment: A <package>+<version> filename "fragment" (stem) or

33 egg fragment.

34 :param canonical_name: The package's canonical name.

35

36 This function is needed since the canonicalized name does not necessarily

37 have the same length as the egg info's name part. An example::

38

39 >>> fragment = "foo__bar-1.0"

40 >>> canonical_name = "foo-bar"

41 >>> _find_name_version_sep(fragment, canonical_name)

42 8

43 """

44 # Project name and version must be separated by one single dash. Find all

45 # occurrences of dashes; if the string in front of it matches the canonical

46 # name, this is the one separating the name and version parts.

47 for i, c in enumerate(fragment): 47 ↛ 52 line 47 didn't jump to line 52 because the loop on line 47 didn't complete

48 if c != "-":

49 continue

50 if canonicalize_name(fragment[:i]) == canonical_name:

51 return i

52 msg = f"{fragment} does not match {canonical_name}"

53 raise ValueError(msg)

54

55

56 def _extract_version_from_fragment(fragment: str, canonical_name: str) -> str | None:

57 """Parse the version string from a <package>+<version> filename

58 "fragment" (stem) or egg fragment.

59

60 :param fragment: The string to parse. E.g. foo-2.1

61 :param canonical_name: The canonicalized name of the package this

62 belongs to.

63 """

64 try:

65 version_start = _find_name_version_sep(fragment, canonical_name) + 1

66 except ValueError:

67 return None

68 version = fragment[version_start:]

69 if not version: 69 ↛ 70 line 69 didn't jump to line 70 because the condition on line 69 was never true

70 return None

71 return version

72

73

74 extract_version_from_fragment = _extract_version_from_fragment