From 1368fa4c3b980656752e6273aeab239ed6af2e32 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 14 Jul 2020 21:07:09 +0300 Subject: [PATCH] Implement new fields (id, ownername, url, requirements) for PackageSpec API --- platformio/commands/package.py | 6 +- platformio/package/spec.py | 134 +++++++++++++++++++++++++++------ tests/package/test_spec.py | 112 ++++++++++++++++++++++++--- 3 files changed, 215 insertions(+), 37 deletions(-) diff --git a/platformio/commands/package.py b/platformio/commands/package.py index 5a3092e2..b5e4ad06 100644 --- a/platformio/commands/package.py +++ b/platformio/commands/package.py @@ -78,7 +78,7 @@ def package_publish(package, owner, released_at, private, notify): @cli.command("unpublish", short_help="Remove a pushed package from the registry") @click.argument( - "package", required=True, metavar="[<@organization>/][@]" + "package", required=True, metavar="[/][@]" ) @click.option( "--type", @@ -96,8 +96,8 @@ def package_unpublish(package, type, undo): # pylint: disable=redefined-builtin response = RegistryClient().unpublish_package( type=type, name=spec.name, - owner=spec.organization, - version=spec.version, + owner=spec.ownername, + version=spec.requirements, undo=undo, ) click.secho(response.get("message"), fg="green") diff --git a/platformio/package/spec.py b/platformio/package/spec.py index f031c71c..f240c843 100644 --- a/platformio/package/spec.py +++ b/platformio/package/spec.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import tarfile -from platformio.compat import get_object_members +from platformio.compat import get_object_members, string_types from platformio.package.manifest.parser import ManifestFileType @@ -55,29 +56,114 @@ class PackageType(object): class PackageSpec(object): - def __init__(self, raw=None, organization=None, name=None, version=None): - if raw is not None: - organization, name, version = self.parse(raw) - - self.organization = organization + def __init__( # pylint: disable=redefined-builtin,too-many-arguments + self, raw=None, ownername=None, id=None, name=None, requirements=None, url=None + ): + self.ownername = ownername + self.id = id self.name = name - self.version = version + self.requirements = requirements + self.url = url + + self._parse(raw) + + def __repr__(self): + return ( + "PackageSpec ".format( + ownername=self.ownername, + id=self.id, + name=self.name, + requirements=self.requirements, + url=self.url, + ) + ) + + def __eq__(self, other): + return all( + [ + self.ownername == other.ownername, + self.id == other.id, + self.name == other.name, + self.requirements == other.requirements, + self.url == other.url, + ] + ) + + def _parse(self, raw): + if raw is None: + return + if not isinstance(raw, string_types): + raw = str(raw) + raw = raw.strip() + + parsers = ( + self._parse_requirements, + self._parse_fixed_name, + self._parse_id, + self._parse_ownername, + self._parse_url, + ) + for parser in parsers: + if raw is None: + break + raw = parser(raw) + + # if name is not fixed, parse it from URL + if not self.name and self.url: + self.name = self._parse_name_from_url(self.url) + elif raw: + # the leftover is a package name + self.name = raw + + def _parse_requirements(self, raw): + if "@" not in raw: + return raw + tokens = raw.rsplit("@", 1) + if any(s in tokens[1] for s in (":", "/")): + return raw + self.requirements = tokens[1].strip() + return tokens[0].strip() + + def _parse_fixed_name(self, raw): + if "=" not in raw or raw.startswith("id="): + return raw + tokens = raw.split("=", 1) + if "/" in tokens[0]: + return raw + self.name = tokens[0].strip() + return tokens[1].strip() + + def _parse_id(self, raw): + if raw.isdigit(): + self.id = int(raw) + return None + if raw.startswith("id="): + return self._parse_id(raw[3:]) + return raw + + def _parse_ownername(self, raw): + if raw.count("/") != 1 or "@" in raw: + return raw + tokens = raw.split("/", 1) + self.ownername = tokens[0].strip() + self.name = tokens[1].strip() + return None + + def _parse_url(self, raw): + if not any(s in raw for s in ("@", ":", "/")): + return raw + self.url = raw.strip() + return None @staticmethod - def parse(raw): - organization = None - name = None - version = None - raw = raw.strip() - if raw.startswith("@") and "/" in raw: - tokens = raw[1:].split("/", 1) - organization = tokens[0].strip() - raw = tokens[1] - if "@" in raw: - name, version = raw.split("@", 1) - name = name.strip() - version = version.strip() - else: - name = raw.strip() - - return organization, name, version + def _parse_name_from_url(url): + if url.endswith("/"): + url = url[:-1] + for c in ("#", "?"): + if c in url: + url = url[: url.index(c)] + name = os.path.basename(url) + if "." in name: + return name.split(".", 1)[0].strip() + return name diff --git a/tests/package/test_spec.py b/tests/package/test_spec.py index 1886a836..dce89d7f 100644 --- a/tests/package/test_spec.py +++ b/tests/package/test_spec.py @@ -15,13 +15,105 @@ from platformio.package.spec import PackageSpec -def test_parser(): - inputs = [ - ("foo", (None, "foo", None)), - ("@org/foo", ("org", "foo", None)), - ("@org/foo @ 1.2.3", ("org", "foo", "1.2.3")), - ("bar @ 1.2.3", (None, "bar", "1.2.3")), - ("cat@^1.2", (None, "cat", "^1.2")), - ] - for raw, result in inputs: - assert PackageSpec.parse(raw) == result +def test_ownername(): + assert PackageSpec("alice/foo library") == PackageSpec( + ownername="alice", name="foo library" + ) + assert PackageSpec(" bob / bar ") == PackageSpec(ownername="bob", name="bar") + + +def test_id(): + assert PackageSpec(13) == PackageSpec(id=13) + assert PackageSpec("20") == PackageSpec(id=20) + assert PackageSpec("id=199") == PackageSpec(id=199) + + +def test_name(): + assert PackageSpec("foo") == PackageSpec(name="foo") + assert PackageSpec(" bar-24 ") == PackageSpec(name="bar-24") + + +def test_requirements(): + assert PackageSpec("foo@1.2.3") == PackageSpec(name="foo", requirements="1.2.3") + assert PackageSpec("bar @ ^1.2.3") == PackageSpec(name="bar", requirements="^1.2.3") + assert PackageSpec("13 @ ~2.0") == PackageSpec(id=13, requirements="~2.0") + assert PackageSpec("id=20 @ !=1.2.3,<2.0") == PackageSpec( + id=20, requirements="!=1.2.3,<2.0" + ) + + +def test_local_urls(): + assert PackageSpec("file:///tmp/foo.tar.gz") == PackageSpec( + url="file:///tmp/foo.tar.gz", name="foo" + ) + assert PackageSpec("customName=file:///tmp/bar.zip") == PackageSpec( + url="file:///tmp/bar.zip", name="customName" + ) + assert PackageSpec("file:///tmp/some-lib/") == PackageSpec( + url="file:///tmp/some-lib/", name="some-lib" + ) + assert PackageSpec("file:///tmp/foo.tar.gz@~2.3.0-beta.1") == PackageSpec( + url="file:///tmp/foo.tar.gz", name="foo", requirements="~2.3.0-beta.1" + ) + + +def test_external_urls(): + assert PackageSpec( + "https://github.com/platformio/platformio-core/archive/develop.zip" + ) == PackageSpec( + url="https://github.com/platformio/platformio-core/archive/develop.zip", + name="develop", + ) + assert PackageSpec( + "https://github.com/platformio/platformio-core/archive/develop.zip?param=value" + " @ !=2" + ) == PackageSpec( + url="https://github.com/platformio/platformio-core/archive/" + "develop.zip?param=value", + name="develop", + requirements="!=2", + ) + assert PackageSpec( + "platformio-core=" + "https://github.com/platformio/platformio-core/archive/develop.tar.gz@4.4.0" + ) == PackageSpec( + url="https://github.com/platformio/platformio-core/archive/develop.tar.gz", + name="platformio-core", + requirements="4.4.0", + ) + + +def test_vcs_urls(): + assert PackageSpec( + "https://github.com/platformio/platformio-core.git" + ) == PackageSpec( + name="platformio-core", url="https://github.com/platformio/platformio-core.git", + ) + assert PackageSpec( + "wolfSSL=https://os.mbed.com/users/wolfSSL/code/wolfSSL/" + ) == PackageSpec( + name="wolfSSL", url="https://os.mbed.com/users/wolfSSL/code/wolfSSL/", + ) + assert PackageSpec( + "git+https://github.com/platformio/platformio-core.git#master" + ) == PackageSpec( + name="platformio-core", + url="git+https://github.com/platformio/platformio-core.git#master", + ) + assert PackageSpec( + "core=git+ssh://github.com/platformio/platformio-core.git#v4.4.0@4.4.0" + ) == PackageSpec( + name="core", + url="git+ssh://github.com/platformio/platformio-core.git#v4.4.0", + requirements="4.4.0", + ) + assert PackageSpec("git@github.com:platformio/platformio-core.git") == PackageSpec( + name="platformio-core", url="git@github.com:platformio/platformio-core.git", + ) + assert PackageSpec( + "pkg=git+git@github.com:platformio/platformio-core.git @ ^1.2.3,!=5" + ) == PackageSpec( + name="pkg", + url="git+git@github.com:platformio/platformio-core.git", + requirements="^1.2.3,!=5", + )