Skip to content

Commit 16c1a60

Browse files
committed
Fix bug in packages
1 parent 6585fd2 commit 16c1a60

File tree

10 files changed

+129
-73
lines changed

10 files changed

+129
-73
lines changed

ecosystem/github.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""GitHub section."""
22

3-
from urllib.parse import ParseResult
43
from re import match
54
from functools import reduce
65
from jsonpath import findall
@@ -12,6 +11,7 @@
1211
parse_github_package_ids,
1312
parse_github_dependants,
1413
parse_github_front_page,
14+
URL,
1515
)
1616

1717

@@ -74,12 +74,12 @@ def to_dict(self) -> dict:
7474
return dictionary
7575

7676
@classmethod
77-
def from_url(cls, github_project_url: ParseResult):
77+
def from_url(cls, github_project_url: URL):
7878
"""
7979
Builds a GitHubSection from an url. Returns None
8080
if the given url is not a GitHub url
8181
"""
82-
if "github.com" not in github_project_url:
82+
if "github.com" not in github_project_url.hostname:
8383
# github_project_url is not a GitHub URL
8484
return None
8585

ecosystem/julia.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from .serializable import JsonSerializable
1515
from .error_handling import EcosystemError
16-
from .request import request_json, parse_url, parse_juliapackages, find_first_in_csv_gz
16+
from .request import request_json, URL, parse_juliapackages, find_first_in_csv_gz
1717

1818

1919
class JuliaData(JsonSerializable):
@@ -174,7 +174,7 @@ def get_juliahub_url(self):
174174
try:
175175
empty = request_json(url, parser=lambda x: {})
176176
if empty == {}:
177-
self.juliahub_url = parse_url(url).geturl()
177+
self.juliahub_url = URL(url)
178178
except EcosystemError:
179179
self.juliahub_url = None
180180

@@ -198,7 +198,7 @@ def get_general_registry_url(self):
198198
try:
199199
empty = request_json(url, parser=lambda x: {})
200200
if empty == {}:
201-
self.general_registry_url = parse_url(url).geturl()
201+
self.general_registry_url = URL(url)
202202
except EcosystemError:
203203
self.general_registry_url = None
204204

ecosystem/member.py

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,66 @@
11
"""Submission model."""
22

33
import pprint
4-
from dataclasses import dataclass, fields
54
from uuid import uuid4
6-
from urllib.parse import urlparse
75

86
from .julia import JuliaData
97
from .serializable import JsonSerializable, parse_datetime
108
from .github import GitHubData
119
from .pypi import PyPIData
10+
from .request import URL
1211

1312

14-
@dataclass
15-
class Member(JsonSerializable):
13+
class Member(JsonSerializable): # pylint: disable=too-many-instance-attributes
1614
"""main Members class that represent a single entry in the Ecosystem."""
1715

18-
# pylint: disable=too-many-instance-attributes
19-
name: str
20-
url: str | None = None
21-
description: str | None = None
22-
licence: str | None = None
23-
contact_info: str | None = None
24-
affiliations: str | None = None
25-
labels: list[str] | None = None
26-
ibm_maintained: bool = False
27-
created_at: int | None = None
28-
updated_at: int | None = None
29-
website: str | None = None
30-
group: str | None = None
31-
category: str | None = None
32-
reference_paper: str | None = None
33-
documentation: str | None = None
34-
packages: list[str] | None = None
35-
uuid: str | None = None
36-
github: GitHubData | None = None
37-
pypi: dict[str, PyPIData] | None = None
38-
julia: JuliaData | None = None
39-
40-
def __post_init__(self):
16+
def __init__( # pylint: disable=too-many-arguments, too-many-locals
17+
self,
18+
name: str,
19+
url: str | URL | None = None,
20+
description: str | None = None,
21+
licence: str | None = None,
22+
contact_info: str | None = None,
23+
affiliations: str | None = None,
24+
labels: list[str] | None = None,
25+
ibm_maintained: bool = False,
26+
created_at: int | None = None,
27+
updated_at: int | None = None,
28+
website: str | None = None,
29+
group: str | None = None,
30+
category: str | None = None,
31+
reference_paper: URL | None = None,
32+
documentation: URL | None = None,
33+
packages: list[URL] | None = None,
34+
uuid: str | None = None,
35+
github: GitHubData | None = None,
36+
pypi: dict[str, PyPIData] | None = None,
37+
julia: JuliaData | None = None,
38+
):
39+
self.name = name
40+
self.url = URL(url) if isinstance(url, str) else url
41+
self.description = description
42+
self.licence = licence
43+
self.contact_info = contact_info
44+
self.affiliations = affiliations
45+
self.labels = labels
46+
self.ibm_maintained = ibm_maintained
47+
self.created_at = created_at
48+
self.updated_at = updated_at
49+
self.website = website
50+
self.group = group
51+
self.category = category
52+
self.reference_paper = reference_paper
53+
self.documentation = documentation
54+
self.packages = packages
55+
self.uuid = uuid
56+
self.github = github
57+
self.pypi = pypi
58+
self.julia = julia
59+
4160
self.__dict__.setdefault("created_at", parse_datetime("now"))
4261
self.__dict__.setdefault("updated_at", parse_datetime("now"))
4362
if self.github is None:
44-
self.github = GitHubData.from_url(urlparse(self.url))
63+
self.github = GitHubData.from_url(self.url)
4564
if self.uuid is None:
4665
self.uuid = str(uuid4())
4766
if self.labels is None:
@@ -63,7 +82,7 @@ def from_dict(cls, dictionary: dict):
6382
6483
Return: Member
6584
"""
66-
submission_fields = [f.name for f in fields(Member)]
85+
submission_fields = vars(Member)["__static_attributes__"]
6786
filtered_dict = {k: v for k, v in dictionary.items() if k in submission_fields}
6887
if "julia" in filtered_dict:
6988
filtered_dict["julia"] = JuliaData.from_dict(filtered_dict["julia"])
@@ -108,7 +127,7 @@ def name_id(self):
108127
It is used to create the TOML file name
109128
"""
110129
# TODO: it is not uniq tho. Maybe add a random number at the end? pylint: disable=W0511
111-
repo_dir = self.url.strip("/").split("/")[-1]
130+
repo_dir = self.url.path.rstrip("/").split("/")[-1]
112131
return repo_dir.lower().replace(".", "_")
113132

114133
def update_github(self):
@@ -137,26 +156,17 @@ def from_submission(cls, submission):
137156
"""
138157
Takes a submission object and creates a very basic Member object
139158
"""
140-
# TODO? licence
141-
142-
url = submission.source_url.geturl() if submission.source_url else None
143-
website = submission.home_url.geturl() if submission.home_url else None
144-
reference_paper = (
145-
submission.paper_url.geturl() if submission.paper_url else None
146-
)
147-
documentation = submission.docs_url.geturl() if submission.docs_url else None
148-
149159
return Member(
150160
name=submission.name,
151-
url=url,
161+
url=submission.source_url,
152162
description=submission.description,
153163
contact_info=submission.contact_info,
154164
labels=submission.labels,
155165
ibm_maintained=submission.is_ibm_maintained,
156-
website=website,
166+
website=submission.home_url,
157167
group=submission.category,
158-
reference_paper=reference_paper,
159-
documentation=documentation,
168+
reference_paper=submission.paper_url,
169+
documentation=submission.docs_url,
160170
github=GitHubData.from_url(submission.source_url),
161171
packages=submission.package_urls,
162172
)

ecosystem/request.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def request_json(
2525
"""Requests the JSON in <url> with <headers>"""
2626
if parser is None:
2727
parser = json.loads
28-
url = parse_url(url)
28+
url = URL(url)
2929
headers = headers or {
3030
"Accept": "application/json,"
3131
"application/vnd.github+json,"
@@ -41,10 +41,10 @@ def request_json(
4141
headers["Authorization"] = "token " + token
4242
headers["User-Agent"] = "github.com/Qiskit/ecosystem/"
4343

44-
response = requests.get(url.geturl(), headers=headers, timeout=240)
44+
response = requests.get(str(url), headers=headers, timeout=240)
4545
if not response.ok:
4646
raise EcosystemError(
47-
f"Bad response {url.geturl()}: {response.reason} ({response.status_code})"
47+
f"Bad response {str(url)}: {response.reason} ({response.status_code})"
4848
)
4949
if content_handler:
5050
content = content_handler(response.content)
@@ -53,24 +53,54 @@ def request_json(
5353
return parser(content)
5454

5555

56-
def parse_url(original_url: str):
57-
"""Normalizes and parses a URL"""
58-
if "_No response_" in original_url:
59-
raise EcosystemError(
60-
f"{original_url} does not look like a URL", logger_level=lambda x: x
56+
class URL:
57+
"""Wraps URLs"""
58+
59+
def __init__(self, original_url: str):
60+
self.original_url = original_url
61+
self._parse_result = None
62+
self.logger = True
63+
64+
def parse_url(self):
65+
"""Normalizes and parses a URL"""
66+
logger_level = None if self.logger else lambda x: x
67+
if "_No response_" in self.original_url:
68+
raise EcosystemError(
69+
f"{self.original_url} does not look like a URL",
70+
logger_level=logger_level,
71+
)
72+
73+
url = urlparse(self.original_url)
74+
scheme = "https"
75+
if url.netloc:
76+
netloc, path = url.hostname, url.path
77+
else:
78+
url_path_parts = url.path.split("/")
79+
netloc = url_path_parts[0]
80+
path = "/".join(url_path_parts[1:])
81+
self._parse_result = urlparse(
82+
urlunparse((scheme, netloc.lower(), path, url.params, url.query, ""))
6183
)
6284

63-
url = urlparse(original_url)
64-
scheme = "https"
65-
if url.netloc:
66-
netloc, path = url.hostname, url.path
67-
else:
68-
url_path_parts = url.path.split("/")
69-
netloc = url_path_parts[0]
70-
path = "/".join(url_path_parts[1:])
71-
return urlparse(
72-
urlunparse((scheme, netloc.lower(), path, url.params, url.query, ""))
73-
)
85+
@property
86+
def hostname(self):
87+
""" Hostname part of the URL """
88+
if self._parse_result is None:
89+
self.parse_url()
90+
return self._parse_result.hostname
91+
92+
@property
93+
def path(self):
94+
""" Path part of the URL """
95+
if self._parse_result is None:
96+
self.parse_url()
97+
return self._parse_result.path
98+
99+
def __eq__(self, other):
100+
return str(self) == str(other)
101+
102+
def __str__(self):
103+
return self._parse_result.geturl() if self._parse_result else self.original_url
74104

75105

76106
def parse_github_front_page(html_text):

ecosystem/resources/members/qiskit_jl.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ version = "0.3.0"
2828
license = "Apache-2.0"
2929
owner = "Qiskit"
3030
release_date = 2025-11-01
31+
juliahub_url = "<ecosystem.request.URL object at 0x10a53b470>"
32+
general_registry_url = "<ecosystem.request.URL object at 0x10a5384d0>"
3133
uuid = "91d9a17d-f964-4b6c-a3c4-2f4cfdea2c95"

ecosystem/resources/members/qiskitopt_jl.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ version = "0.3.0"
3131
license = "MIT"
3232
owner = "psrenergy"
3333
release_date = 2024-05-01
34+
juliahub_url = "<ecosystem.request.URL object at 0x10a7550d0>"
35+
general_registry_url = "<ecosystem.request.URL object at 0x10a2f1250>"
3436
uuid = "81b20daf-e62b-4502-a0d1-aa084de80e33"
3537
estimated_unique_users = "20"

ecosystem/serializable.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from abc import ABC
44
from datetime import datetime
55

6+
from ecosystem.request import URL
7+
68

79
class JsonSerializable(ABC):
810
"""Classes that can be serialized as json."""
@@ -24,7 +26,7 @@ def from_dict(cls, dictionary: dict):
2426
"""
2527
return cls(**dictionary)
2628

27-
def to_dict(self) -> dict:
29+
def to_dict(self) -> dict: # pylint: disable=too-many-branches
2830
"""Converts Object to dict."""
2931
result = {}
3032
for key, val in self.__dict__.items():
@@ -35,6 +37,8 @@ def to_dict(self) -> dict:
3537
for item in val:
3638
if isinstance(item, JsonSerializable):
3739
element.append(item.to_dict())
40+
if isinstance(item, URL):
41+
element.append(str(item))
3842
else:
3943
element.append(item)
4044
elif isinstance(val, dict):
@@ -47,6 +51,8 @@ def to_dict(self) -> dict:
4751
continue
4852
elif isinstance(val, JsonSerializable):
4953
element = val.to_dict()
54+
elif isinstance(val, URL):
55+
element = str(val)
5056
else:
5157
element = val
5258
result[key] = element

ecosystem/submission.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import yaml
77

88
from .error_handling import EcosystemError
9-
from .request import parse_url
9+
from .request import URL
1010

1111

1212
@dataclass
@@ -100,14 +100,14 @@ def _parse_section(section: str, label_to_id: dict[str, str]):
100100
content = raw_content[0].startswith("- [x]")
101101
elif field_id.endswith("_url"):
102102
try:
103-
content = parse_url(raw_content[0]) if raw_content else None
103+
content = URL(raw_content[0]) if raw_content else None
104104
except EcosystemError:
105105
content = None
106106
elif field_id.endswith("_urls"):
107107
content = []
108108
for url in raw_content:
109109
try:
110-
content.append(parse_url(url))
110+
content.append(URL(url))
111111
except EcosystemError:
112112
pass
113113
else:

tests/daos/test_dao.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_repository_insert_and_delete(self):
3838

3939
# insert entry
4040
dao.write(main_repo)
41-
fetched_repo = dao.get_by_url(main_repo.url)
41+
fetched_repo = dao.get_by_url(str(main_repo.url))
4242
self.assertEqual(main_repo, fetched_repo)
4343

4444
# delete entry

tests/test_cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ def test_add_member_from_issue(self):
7171
"reference_paper": "https://arxiv.org/abs/5555.22222",
7272
"github": {"owner": "somebody", "repo": "banana-compiler"},
7373
"group": "circuit manipulation",
74+
"packages": [
75+
"https://pypi.org/project/banana-compiler",
76+
"https://pypi.org/project/banana-compiler-hpc",
77+
"https://crates.io/crates/rusty-banana-compiler",
78+
"https://marketplace.visualstudio.com/items?itemName=banana-code-assistance",
79+
],
7480
}
7581
self.assertEqual(len(retrieved_repos), 1)
7682
retrieved = list(retrieved_repos)[0].to_dict()

0 commit comments

Comments
 (0)