|
20 | 20 | import json |
21 | 21 | import sys |
22 | 22 | from json import JSONDecodeError |
23 | | -from typing import Optional |
| 23 | +from typing import Optional, Tuple |
| 24 | +from urllib.parse import urlparse |
24 | 25 |
|
25 | 26 | if sys.version_info >= (3, 8): |
26 | 27 | from typing import TypedDict |
27 | 28 | else: |
28 | 29 | from typing_extensions import TypedDict |
29 | 30 |
|
30 | | -from urllib.parse import urlparse |
31 | | - |
32 | 31 |
|
33 | 32 | class CondaPackage(TypedDict): |
34 | 33 | """ |
@@ -72,56 +71,72 @@ def parse_conda_list_str_to_conda_package(conda_list_str: str) -> Optional[Conda |
72 | 71 |
|
73 | 72 | line = conda_list_str.strip() |
74 | 73 |
|
75 | | - if line[0:1] == '#' or line[0:1] == '@' or len(line) == 0: |
| 74 | + if '' == line or line[0] in ['#', '@']: |
76 | 75 | # Skip comments, @EXPLICT or empty lines |
77 | 76 | return None |
78 | 77 |
|
79 | 78 | # Remove any hash |
80 | 79 | package_hash = None |
81 | 80 | if '#' in line: |
82 | | - hash_parts = line.split('#') |
83 | | - if len(hash_parts) > 1: |
84 | | - package_hash = hash_parts.pop() |
85 | | - line = ''.join(hash_parts) |
| 81 | + *_line_parts, package_hash = line.split('#') |
| 82 | + line = ''.join(*_line_parts) |
86 | 83 |
|
87 | 84 | package_parts = line.split('/') |
88 | | - package_name_version_build_string = package_parts.pop() |
89 | | - package_arch = package_parts.pop() |
90 | | - package_url = urlparse('/'.join(package_parts)) |
| 85 | + if len(package_parts) < 2: |
| 86 | + raise ValueError(f'Unexpected format in {package_parts}') |
| 87 | + *_package_url_parts, package_arch, package_name_version_build_string = package_parts |
| 88 | + package_url = urlparse('/'.join(_package_url_parts)) |
91 | 89 |
|
92 | | - try: |
93 | | - package_nvbs_parts = package_name_version_build_string.split('-') |
94 | | - build_number_with_opt_string = package_nvbs_parts.pop() |
95 | | - if '.' in build_number_with_opt_string: |
96 | | - # Remove any .conda at the end if present or other package type eg .tar.gz |
97 | | - pos = build_number_with_opt_string.find('.') |
98 | | - build_number_with_opt_string = build_number_with_opt_string[0:pos] |
99 | | - |
100 | | - build_string: str |
101 | | - build_number: Optional[int] |
102 | | - |
103 | | - if '_' in build_number_with_opt_string: |
104 | | - bnbs_parts = build_number_with_opt_string.split('_') |
105 | | - # Build number will be the last part - check if it's an integer |
106 | | - # Updated logic given https://github.com/CycloneDX/cyclonedx-python-lib/issues/65 |
107 | | - candidate_build_number: str = bnbs_parts.pop() |
108 | | - if candidate_build_number.isdigit(): |
109 | | - build_number = int(candidate_build_number) |
110 | | - build_string = build_number_with_opt_string |
111 | | - else: |
112 | | - build_number = None |
113 | | - build_string = build_number_with_opt_string |
114 | | - else: |
115 | | - build_string = '' |
116 | | - build_number = int(build_number_with_opt_string) |
117 | | - |
118 | | - build_version = package_nvbs_parts.pop() |
119 | | - package_name = '-'.join(package_nvbs_parts) |
120 | | - except IndexError as e: |
121 | | - raise ValueError(f'Error parsing {package_nvbs_parts} from {conda_list_str}') from e |
| 90 | + package_name, build_version, build_string = split_package_string(package_name_version_build_string) |
| 91 | + build_string, build_number = split_package_build_string(build_string) |
122 | 92 |
|
123 | 93 | return CondaPackage( |
124 | 94 | base_url=package_url.geturl(), build_number=build_number, build_string=build_string, |
125 | 95 | channel=package_url.path[1:], dist_name=f'{package_name}-{build_version}-{build_string}', |
126 | 96 | name=package_name, platform=package_arch, version=build_version, md5_hash=package_hash |
127 | 97 | ) |
| 98 | + |
| 99 | + |
| 100 | +def split_package_string(package_name_version_build_string: str) -> Tuple[str, str, str]: |
| 101 | + """Helper method for parsing package_name_version_build_string. |
| 102 | +
|
| 103 | + Returns: |
| 104 | + Tuple (package_name, build_version, build_string) |
| 105 | + """ |
| 106 | + package_nvbs_parts = package_name_version_build_string.split('-') |
| 107 | + if len(package_nvbs_parts) < 3: |
| 108 | + raise ValueError(f'Unexpected format in {package_nvbs_parts}') |
| 109 | + |
| 110 | + *_package_name_parts, build_version, build_string = package_nvbs_parts |
| 111 | + package_name = '-'.join(_package_name_parts) |
| 112 | + |
| 113 | + _pos = build_string.find('.') |
| 114 | + if _pos >= 0: |
| 115 | + # Remove any .conda at the end if present or other package type eg .tar.gz |
| 116 | + build_string = build_string[0:_pos] |
| 117 | + |
| 118 | + return package_name, build_version, build_string |
| 119 | + |
| 120 | + |
| 121 | +def split_package_build_string(build_string: str) -> Tuple[str, Optional[int]]: |
| 122 | + """Helper method for parsing build_string. |
| 123 | +
|
| 124 | + Returns: |
| 125 | + Tuple (build_string, build_number) |
| 126 | + """ |
| 127 | + |
| 128 | + if '' == build_string: |
| 129 | + return '', None |
| 130 | + |
| 131 | + if build_string.isdigit(): |
| 132 | + return '', int(build_string) |
| 133 | + |
| 134 | + _pos = build_string.rindex('_') if '_' in build_string else -1 |
| 135 | + if _pos >= 1: |
| 136 | + # Build number will be the last part - check if it's an integer |
| 137 | + # Updated logic given https://github.com/CycloneDX/cyclonedx-python-lib/issues/65 |
| 138 | + build_number = build_string[_pos + 1:] |
| 139 | + if build_number.isdigit(): |
| 140 | + return build_string, int(build_number) |
| 141 | + |
| 142 | + return build_string, None |
0 commit comments