Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ warn_unused_configs = True
# should be added to this whitelist.
# see https://github.com/python-poetry/poetry/pull/4510.

[mypy-poetry.config.*]
[mypy-poetry.config.file_config_source]
ignore_errors = True

[mypy-poetry.console.*]
Expand All @@ -34,16 +34,13 @@ ignore_errors = True
[mypy-poetry.mixology.*]
ignore_errors = True

[mypy-poetry.packages.*]
ignore_errors = True

[mypy-poetry.publishing.*]
[mypy-poetry.packages.locker]
ignore_errors = True

[mypy-poetry.puzzle.*]
ignore_errors = True

[mypy-poetry.repositories.*]
[mypy-poetry.repositories.installed_repository]
ignore_errors = True

[mypy-poetry.utils.*]
Expand Down
19 changes: 6 additions & 13 deletions poetry/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
from .dict_config_source import DictConfigSource


_NOT_SET = object()


def boolean_validator(val: str) -> bool:
return val in {"true", "false", "1", "0"}

Expand All @@ -27,7 +24,7 @@ def boolean_normalizer(val: str) -> bool:

class Config:

default_config = {
default_config: Dict[str, Any] = {
"cache-dir": str(CACHE_DIR),
"virtualenvs": {
"create": True,
Expand All @@ -45,12 +42,8 @@ def __init__(
self._config = deepcopy(self.default_config)
self._use_environment = use_environment
self._base_dir = base_dir
self._config_source = DictConfigSource()
self._auth_config_source = DictConfigSource()

@property
Comment thread
dimbleby marked this conversation as resolved.
def name(self) -> str:
return str(self._file.path)
self._config_source: ConfigSource = DictConfigSource()
self._auth_config_source: ConfigSource = DictConfigSource()

@property
def config(self) -> Dict:
Expand Down Expand Up @@ -114,9 +107,9 @@ def get(self, setting_name: str, default: Any = None) -> Any:
env = "POETRY_{}".format(
"_".join(k.upper().replace("-", "_") for k in keys)
)
value = os.getenv(env, _NOT_SET)
if value is not _NOT_SET:
return self.process(self._get_normalizer(setting_name)(value))
env_value = os.getenv(env)
Comment thread
dimbleby marked this conversation as resolved.
if env_value is not None:
return self.process(self._get_normalizer(setting_name)(env_value))

value = self._config
for key in keys:
Expand Down
2 changes: 1 addition & 1 deletion poetry/config/dict_config_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class DictConfigSource(ConfigSource):
def __init__(self) -> None:
self._config = {}
self._config: Dict[str, Any] = {}

@property
def config(self) -> Dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion poetry/inspection/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __init__(
platform: Optional[str] = None,
requires_dist: Optional[List[str]] = None,
requires_python: Optional[str] = None,
files: Optional[List[str]] = None,
files: Optional[List[Dict[str, str]]] = None,
cache_version: Optional[str] = None,
):
self.name = name
Expand Down
2 changes: 0 additions & 2 deletions poetry/packages/project_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,3 @@ def set_version(
else:
self._version = version
self._pretty_version = pretty_version or version.text

return self
2 changes: 1 addition & 1 deletion poetry/publishing/publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def publish(
password: Optional[str],
cert: Optional[Path] = None,
client_cert: Optional[Path] = None,
dry_run: Optional[bool] = False,
dry_run: bool = False,
) -> None:
if not repository_name:
url = "https://upload.pypi.org/legacy/"
Expand Down
31 changes: 17 additions & 14 deletions poetry/publishing/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def __init__(self, poetry: "Poetry", io: "NullIO") -> None:
self._poetry = poetry
self._package = poetry.package
self._io = io
self._username = None
self._password = None
self._username: Optional[str] = None
self._password: Optional[str] = None

@property
def user_agent(self) -> str:
Expand Down Expand Up @@ -89,23 +89,27 @@ def files(self) -> List[Path]:

return sorted(wheels + tars)

def auth(self, username: str, password: str) -> None:
def auth(self, username: Optional[str], password: Optional[str]) -> None:
self._username = username
self._password = password

def make_session(self) -> requests.Session:
session = requests.session()
if self.is_authenticated():
session.auth = (self._username, self._password)
auth = self.get_auth()
if auth is not None:
session.auth = auth

session.headers["User-Agent"] = self.user_agent
for scheme in ("http://", "https://"):
session.mount(scheme, self.adapter)

return session

def is_authenticated(self) -> bool:
return self._username is not None and self._password is not None
def get_auth(self) -> Optional[Tuple[str, str]]:
Comment thread
dimbleby marked this conversation as resolved.
if self._username is None or self._password is None:
return None

return (self._username, self._password)

def upload(
self,
Expand Down Expand Up @@ -147,16 +151,15 @@ def post_data(self, file: Path) -> Dict[str, Any]:

md5_digest = md5_hash.hexdigest()
sha2_digest = sha256_hash.hexdigest()
blake2_256_digest: Optional[str] = None
if _has_blake2:
blake2_256_digest = blake2_256_hash.hexdigest()
else:
blake2_256_digest = None

py_version: Optional[str] = None
if file_type == "bdist_wheel":
wheel_info = wheel_file_re.match(file.name)
py_version = wheel_info.group("pyver")
else:
py_version = None
if wheel_info is not None:
py_version = wheel_info.group("pyver")

data = {
# identify release
Expand Down Expand Up @@ -231,7 +234,7 @@ def _upload_file(
}
)

data_to_send = self._prepare_data(data)
data_to_send: List[Tuple[str, Any]] = self._prepare_data(data)

with file.open("rb") as fp:
data_to_send.append(
Expand All @@ -256,7 +259,7 @@ def _upload_file(
allow_redirects=False,
headers={"Content-Type": monitor.content_type},
)
if dry_run or 200 <= resp.status_code < 300:
if resp is None or 200 <= resp.status_code < 300:
bar.set_format(
f" - Uploading <c1>{file.name}</c1> <fg=green>%percent%%</>"
)
Expand Down
10 changes: 5 additions & 5 deletions poetry/repositories/base_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@

class BaseRepository:
def __init__(self) -> None:
self._packages = []
self._packages: List[Package] = []

@property
def packages(self) -> List["Package"]:
return self._packages

def has_package(self, package: "Package") -> None:
def has_package(self, package: "Package") -> bool:
raise NotImplementedError()

def package(
self, name: str, version: str, extras: Optional[List[str]] = None
) -> None:
) -> "Package":
raise NotImplementedError()

def find_packages(self, dependency: "Dependency") -> None:
def find_packages(self, dependency: "Dependency") -> List["Package"]:
raise NotImplementedError()

def search(self, query: str) -> None:
def search(self, query: str) -> List["Package"]:
raise NotImplementedError()
29 changes: 8 additions & 21 deletions poetry/repositories/legacy_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import warnings

from collections import defaultdict
from html import unescape
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import Dict
from typing import Iterator
from typing import List
from typing import Optional
from urllib.parse import quote

import requests
import requests.auth
Expand Down Expand Up @@ -43,23 +45,6 @@
if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency

try:
from html import unescape
except ImportError:
try:
from html.parser import HTMLParser
except ImportError:
from HTMLParser import HTMLParser

unescape = HTMLParser().unescape


try:
from urllib.parse import quote
except ImportError:
from urllib import quote


with warnings.catch_warnings():
warnings.simplefilter("ignore")
import html5lib
Expand Down Expand Up @@ -164,6 +149,8 @@ def clean_link(self, url: str) -> str:
return self._clean_re.sub(lambda match: "%%%2x" % ord(match.group(0)), url)


# TODO: revisit whether the LegacyRepository should inherit from PyPiRepository.
# <https://github.com/python-poetry/poetry/pull/4755#discussion_r748865374>.
class LegacyRepository(PyPiRepository):
def __init__(
self,
Expand Down Expand Up @@ -269,7 +256,7 @@ def find_packages(self, dependency: "Dependency") -> List[Package]:
if self._cache.store("matches").has(key):
versions = self._cache.store("matches").get(key)
else:
page = self._get("/{}/".format(dependency.name.replace(".", "-")))
page = self._get_page("/{}/".format(dependency.name.replace(".", "-")))
if page is None:
return []

Expand Down Expand Up @@ -338,14 +325,14 @@ def package(
return package

def find_links_for_package(self, package: Package) -> List[Link]:
page = self._get("/{}/".format(package.name.replace(".", "-")))
page = self._get_page("/{}/".format(package.name.replace(".", "-")))
if page is None:
return []

return list(page.links_for_version(package.version))

def _get_release_info(self, name: str, version: str) -> dict:
page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-")))
page = self._get_page("/{}/".format(canonicalize_name(name).replace(".", "-")))
if page is None:
raise PackageNotFound(f'No package named "{name}"')

Expand Down Expand Up @@ -419,7 +406,7 @@ def _get_release_info(self, name: str, version: str) -> dict:

return data.asdict()

def _get(self, endpoint: str) -> Optional[Page]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was wrong with _get?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed LegacyRepositoy._get() as LegacyRepository._get_page(): so far as I can see it has little in common with its parent class's PyPiRepository._get(), and mypy complains about them not being the same

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the correct solution here is going to be refactoring LegacyRepository to not inherit from PypiRepository. I'd suggest doing that in a follow-up PR, and leaving some # type: and # FIXME annotations here in the meantime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if that's the right long-term fix, get_page() seems to me a sensible name for this method: which gets a Page. Indeed separating this class from the PyPiRepository would leave less reason for their methods to have the same names.

So I'd prefer to leave this change in place: so that we can leave this file mypy-clean - rather than having to whitelist it and allow other errors to slip back in.

(I do not expect to follow up with the change that you propose. Not that I disagree with it; but it's more work than I intend to be taking on here.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's sounds logic -- let's go ahead and add a # TODO: annotation above the class as a reminder that this needs to be revisited.

def _get_page(self, endpoint: str) -> Optional[Page]:
url = self._url + endpoint
try:
response = self.session.get(url)
Expand Down
6 changes: 4 additions & 2 deletions poetry/repositories/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ def __init__(
if repositories is None:
repositories = []

self._lookup: Dict[str, int] = {}
self._lookup: Dict[Optional[str], int] = {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why there is an Optional here -- None will all hash to the same type in the dict, and I don't think it should ever end up here. I suspect the error is found elsewhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One place where I have just updated the annotation to match the code but which smells funny to me is in pool.py. Is it really intended that the repository_name indexing self._lookup might be None? I tried asserting that it was not, but lots of testcases failed...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this is unintentional behavior (even if it doesn't cause a bug). For now I'd just whitelist it with an annotation, and circle back to address this on a follow-up PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in two minds about this.

  • The advantage of changing the annotation as I have it is that we can then remove this file from the whitelist, and be confident that further typing errors will not happen.
  • The advantage of your suggestion is that it reminds us that there is a problem here.

Well, why not both? Suggest that we leave the annotation in place for now - it does, after all, reflect what the code is actually doing - but also add a # FIXME saying "surely there's something wrong here".

(Again, I do not expect to follow-up with any fix for that - this looks like quite a pervasive change).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the compromise here is good. Fair enough!

self._repositories: List[Repository] = []
self._default = False
self._has_primary_repositories = False
self._secondary_start_idx = None
self._secondary_start_idx: Optional[int] = None

for repository in repositories:
self.add_repository(repository)
Expand Down Expand Up @@ -65,6 +65,8 @@ def add_repository(
"""
Adds a repository to the pool.
"""
# FIXME: surely it's a problem that the repository name can be None here?
# All nameless repositories will collide in self._lookup.
repository_name = (
repository.name.lower() if repository.name is not None else None
)
Expand Down
2 changes: 1 addition & 1 deletion poetry/repositories/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, packages: List["Package"] = None, name: str = None) -> None:
self.add_package(package)

@property
def name(self) -> str:
def name(self) -> Optional[str]:
Comment thread
neersighted marked this conversation as resolved.
return self._name

def package(
Expand Down
Loading