Merge branch 'release/v6.1.14'

This commit is contained in:
Ivan Kravets
2024-03-21 21:11:24 +02:00
53 changed files with 458 additions and 244 deletions

View File

@ -7,6 +7,7 @@ Release Notes
.. |INTERPOLATION| replace:: `Interpolation of Values <https://docs.platformio.org/en/latest/projectconf/interpolation.html>`__ .. |INTERPOLATION| replace:: `Interpolation of Values <https://docs.platformio.org/en/latest/projectconf/interpolation.html>`__
.. |UNITTESTING| replace:: `Unit Testing <https://docs.platformio.org/en/latest/advanced/unit-testing/index.html>`__ .. |UNITTESTING| replace:: `Unit Testing <https://docs.platformio.org/en/latest/advanced/unit-testing/index.html>`__
.. |DEBUGGING| replace:: `Debugging <https://docs.platformio.org/en/latest/plus/debugging.html>`__ .. |DEBUGGING| replace:: `Debugging <https://docs.platformio.org/en/latest/plus/debugging.html>`__
.. |STATICCODEANALYSIS| replace:: `Static Code Analysis <https://docs.platformio.org/en/latest/advanced/static-code-analysis/index.html>`__
.. _release_notes_6: .. _release_notes_6:
@ -17,6 +18,19 @@ Unlock the true potential of embedded software development with
PlatformIO's collaborative ecosystem, embracing declarative principles, PlatformIO's collaborative ecosystem, embracing declarative principles,
test-driven methodologies, and modern toolchains for unrivaled success. test-driven methodologies, and modern toolchains for unrivaled success.
6.1.14 (2024-03-21)
~~~~~~~~~~~~~~~~~~~
* Introduced the ``--json-output`` option to the `pio test <https://docs.platformio.org/en/latest/core/userguide/cmd_test.html>`__ command, enabling users to generate test results in the JSON format
* Upgraded the build engine to the latest version of SCons (4.7.0) to improve build performance, reliability, and compatibility with other tools and systems (`release notes <https://github.com/SCons/scons/releases/tag/4.7.0>`__)
* Broadened version support for the ``pyelftools`` dependency, enabling compatibility with lower versions and facilitating integration with a wider range of third-party tools (`issue #4834 <https://github.com/platformio/platformio-core/issues/4834>`_)
* Addressed an issue where passing a relative path (``--project-dir``) to the `pio project init <https://docs.platformio.org/en/latest/core/userguide/project/cmd_init.html>`__ command resulted in an error (`issue #4847 <https://github.com/platformio/platformio-core/issues/4847>`_)
* Enhanced |STATICCODEANALYSIS| to accommodate scenarios where custom ``src_dir`` or ``include_dir`` are located outside the project folder (`pull #4874 <https://github.com/platformio/platformio-core/pull/4874>`_)
* Corrected the validation of ``symlink://`` `package specifications <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html#local-folder>`__ , resolving an issue that caused the package manager to repeatedly reinstall dependencies (`pull #4870 <https://github.com/platformio/platformio-core/pull/4870>`_)
* Resolved an issue related to the relative package path in the `pio pkg publish <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_publish.html>`__ command
* Resolved an issue where the |LDF| selected an incorrect library version (`issue #4860 <https://github.com/platformio/platformio-core/issues/4860>`_)
* Resolved an issue with the ``hexlify`` filter in the `device monitor <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html>`__ command, ensuring proper representation of characters with Unicode code points higher than 127 (`issue #4732 <https://github.com/platformio/platformio-core/issues/4732>`_)
6.1.13 (2024-01-12) 6.1.13 (2024-01-12)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

2
docs

Submodule docs updated: 3f02152561...670721e923

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
VERSION = (6, 1, 13) VERSION = (6, 1, 14)
__version__ = ".".join([str(s) for s in VERSION]) __version__ = ".".join([str(s) for s in VERSION])
__title__ = "platformio" __title__ = "platformio"
@ -38,15 +38,6 @@ __registry_mirror_hosts__ = [
] ]
__pioremote_endpoint__ = "ssl:host=remote.platformio.org:port=4413" __pioremote_endpoint__ = "ssl:host=remote.platformio.org:port=4413"
__core_packages__ = {
"contrib-piohome": "~3.4.2",
"contrib-pioremote": "~1.0.0",
"tool-scons": "~4.40600.0",
"tool-cppcheck": "~1.21100.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
}
__check_internet_hosts__ = [ __check_internet_hosts__ = [
"185.199.110.153", # Github.com "185.199.110.153", # Github.com
"88.198.170.159", # platformio.org "88.198.170.159", # platformio.org

View File

@ -48,11 +48,13 @@ def team_list_cmd(orgname, json_output):
table_data.append( table_data.append(
( (
"Members:", "Members:",
", ".join( (
(member.get("username") for member in team.get("members")) ", ".join(
) (member.get("username") for member in team.get("members"))
if team.get("members") )
else "-", if team.get("members")
else "-"
),
) )
) )
click.echo(tabulate(table_data, tablefmt="plain")) click.echo(tabulate(table_data, tablefmt="plain"))

View File

@ -36,6 +36,8 @@ ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE:="0666", ENV{ID_MM_DEVIC
# QinHeng Electronics HL-340 USB-Serial adapter # QinHeng Electronics HL-340 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# QinHeng Electronics CH343 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# QinHeng Electronics CH9102 USB-Serial adapter # QinHeng Electronics CH9102 USB-Serial adapter
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
@ -85,6 +87,8 @@ ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="[01]*", MODE:="0666", ENV{ID_MM_DEVI
# AIR32F103 # AIR32F103
ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# STM32 virtual COM port
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# #
# Debuggers # Debuggers
@ -173,4 +177,4 @@ ATTRS{product}=="*CMSIS-DAP*", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID
ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2107", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2107", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
# Espressif USB JTAG/serial debug unit # Espressif USB JTAG/serial debug unit
ATTRS{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" ATTRS{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

View File

@ -54,7 +54,7 @@ def GetBuildType(env):
modes.append("debug") modes.append("debug")
if "__test" in COMMAND_LINE_TARGETS or env.GetProjectOption("build_type") == "test": if "__test" in COMMAND_LINE_TARGETS or env.GetProjectOption("build_type") == "test":
modes.append("test") modes.append("test")
return "+".join(modes or ["release"]) return ", ".join(modes or ["release"])
def BuildProgram(env): def BuildProgram(env):

View File

@ -309,10 +309,10 @@ class LibBuilderBase:
if not self.dependencies or self._deps_are_processed: if not self.dependencies or self._deps_are_processed:
return return
self._deps_are_processed = True self._deps_are_processed = True
for item in self.dependencies: for dependency in self.dependencies:
found = False found = False
for lb in self.env.GetLibBuilders(): for lb in self.env.GetLibBuilders():
if item["name"] != lb.name: if not lb.is_dependency_compatible(dependency):
continue continue
found = True found = True
if lb not in self.depbuilders: if lb not in self.depbuilders:
@ -322,9 +322,20 @@ class LibBuilderBase:
if not found and self.verbose: if not found and self.verbose:
sys.stderr.write( sys.stderr.write(
"Warning: Ignored `%s` dependency for `%s` " "Warning: Ignored `%s` dependency for `%s` "
"library\n" % (item["name"], self.name) "library\n" % (dependency["name"], self.name)
) )
def is_dependency_compatible(self, dependency):
pkg = PackageItem(self.path)
qualifiers = {"name": self.name, "version": self.version}
if pkg.metadata:
qualifiers = {"name": pkg.metadata.name, "version": pkg.metadata.version}
if pkg.metadata.spec and pkg.metadata.spec.owner:
qualifiers["owner"] = pkg.metadata.spec.owner
return PackageCompatibility.from_dependency(
{k: v for k, v in dependency.items() if k in ("owner", "name", "version")}
).is_compatible(PackageCompatibility(**qualifiers))
def get_search_files(self): def get_search_files(self):
return [ return [
os.path.join(self.src_dir, item) os.path.join(self.src_dir, item)

View File

@ -23,10 +23,10 @@ from SCons.Subst import quote_spaces # pylint: disable=import-error
from platformio.compat import IS_WINDOWS, hashlib_encode_data from platformio.compat import IS_WINDOWS, hashlib_encode_data
# There are the next limits depending on a platform: # There are the next limits depending on a platform:
# - Windows = 8192 # - Windows = 8191
# - Unix = 131072 # - Unix = 131072
# We need ~512 characters for compiler and temporary file paths # We need ~512 characters for compiler and temporary file paths
MAX_LINE_LENGTH = (8192 if IS_WINDOWS else 131072) - 512 MAX_LINE_LENGTH = (8191 if IS_WINDOWS else 131072) - 512
WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")

View File

@ -75,9 +75,11 @@ def LoadPioPlatform(env):
continue continue
env.PrependENVPath( env.PrependENVPath(
"PATH", "PATH",
os.path.join(pkg.path, "bin") (
if os.path.isdir(os.path.join(pkg.path, "bin")) os.path.join(pkg.path, "bin")
else pkg.path, if os.path.isdir(os.path.join(pkg.path, "bin"))
else pkg.path
),
) )
if ( if (
not IS_WINDOWS not IS_WINDOWS

View File

@ -103,10 +103,21 @@ def cli(
"%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v) "%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v)
) )
default_src_filters = [ default_src_filters = []
"+<%s>" % os.path.basename(config.get("platformio", "src_dir")), for d in (
"+<%s>" % os.path.basename(config.get("platformio", "include_dir")), config.get("platformio", "src_dir"),
] config.get("platformio", "include_dir"),
):
try:
default_src_filters.append("+<%s>" % os.path.relpath(d))
except ValueError as exc:
# On Windows if sources are located on a different logical drive
if not json_output and not silent:
click.echo(
"Error: Project cannot be analyzed! The project folder `%s`"
" is located on a different logical drive\n" % d
)
raise exception.ReturnErrorCode(1) from exc
env_src_filters = ( env_src_filters = (
src_filters src_filters
@ -122,9 +133,11 @@ def cli(
silent=silent, silent=silent,
src_filters=env_src_filters, src_filters=env_src_filters,
flags=flags or env_options.get("check_flags"), flags=flags or env_options.get("check_flags"),
severity=[DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]] severity=(
if silent [DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]]
else severity or config.get("env:" + envname, "check_severity"), if silent
else severity or config.get("env:" + envname, "check_severity")
),
skip_packages=skip_packages or env_options.get("check_skip_packages"), skip_packages=skip_packages or env_options.get("check_skip_packages"),
platform_packages=env_options.get("platform_packages"), platform_packages=env_options.get("platform_packages"),
) )
@ -142,9 +155,11 @@ def cli(
result = {"env": envname, "tool": tool, "duration": time()} result = {"env": envname, "tool": tool, "duration": time()}
rc = ct.check( rc = ct.check(
on_defect_callback=None on_defect_callback=(
if (json_output or verbose) None
else lambda defect: click.echo(repr(defect)) if (json_output or verbose)
else lambda defect: click.echo(repr(defect))
)
) )
result["defects"] = ct.get_defects() result["defects"] = ct.get_defects()

View File

@ -19,9 +19,9 @@ import subprocess
import click import click
from platformio import VERSION, __version__, app, exception from platformio import VERSION, __version__, app, exception
from platformio.dependencies import get_pip_dependencies
from platformio.http import fetch_remote_content from platformio.http import fetch_remote_content
from platformio.package.manager.core import update_core_packages from platformio.package.manager.core import update_core_packages
from platformio.pipdeps import get_pip_dependencies
from platformio.proc import get_pythonexe_path from platformio.proc import get_pythonexe_path
PYPI_JSON_URL = "https://pypi.org/pypi/platformio/json" PYPI_JSON_URL = "https://pypi.org/pypi/platformio/json"

View File

@ -17,6 +17,7 @@
import importlib.util import importlib.util
import inspect import inspect
import locale import locale
import os
import shlex import shlex
import sys import sys
@ -41,10 +42,14 @@ else:
if sys.version_info >= (3, 9): if sys.version_info >= (3, 9):
from asyncio import to_thread as aio_to_thread from asyncio import to_thread as aio_to_thread
else: else:
from starlette.concurrency import run_in_threadpool as aio_to_thread try:
from starlette.concurrency import run_in_threadpool as aio_to_thread
except ImportError:
pass
PY2 = sys.version_info[0] == 2 # DO NOT REMOVE IT. ESP8266/ESP32 depend on it PY2 = sys.version_info[0] == 2 # DO NOT REMOVE IT. ESP8266/ESP32 depend on it
PY36 = sys.version_info[0:2] == (3, 6)
IS_CYGWIN = sys.platform.startswith("cygwin") IS_CYGWIN = sys.platform.startswith("cygwin")
IS_WINDOWS = WINDOWS = sys.platform.startswith("win") IS_WINDOWS = WINDOWS = sys.platform.startswith("win")
IS_MACOS = sys.platform.startswith("darwin") IS_MACOS = sys.platform.startswith("darwin")
@ -132,3 +137,12 @@ def path_to_unicode(path):
and custom device monitor filters and custom device monitor filters
""" """
return path return path
def is_proxy_set(socks=False):
for var in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
value = os.getenv(var, os.getenv(var.lower()))
if not value or (socks and not value.startswith("socks5://")):
continue
return True
return False

View File

@ -148,7 +148,9 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
) )
def _load_build_data(self): def _load_build_data(self):
data = load_build_metadata(os.getcwd(), self.env_name, cache=True, debug=True) data = load_build_metadata(
os.getcwd(), self.env_name, cache=True, build_type="debug"
)
if not data: if not data:
raise DebugInvalidOptionsError("Could not load a build configuration") raise DebugInvalidOptionsError("Could not load a build configuration")
return data return data
@ -194,9 +196,11 @@ class DebugConfigBase: # pylint: disable=too-many-instance-attributes
cwd=server_package_dir if server_package else None, cwd=server_package_dir if server_package else None,
executable=result.get("executable"), executable=result.get("executable"),
arguments=[ arguments=[
a.replace("$PACKAGE_DIR", server_package_dir) (
if server_package_dir a.replace("$PACKAGE_DIR", server_package_dir)
else a if server_package_dir
else a
)
for a in result.get("arguments", []) for a in result.get("arguments", [])
], ],
) )

View File

@ -12,11 +12,20 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
import platform import platform
import sys
PY36 = sys.version_info[0:2] == (3, 6) from platformio.compat import PY36, is_proxy_set
def get_core_dependencies():
return {
"contrib-piohome": "~3.4.2",
"contrib-pioremote": "~1.0.0",
"tool-scons": "~4.40700.0",
"tool-cppcheck": "~1.21100.0",
"tool-clangtidy": "~1.150005.0",
"tool-pvs-studio": "~7.18.0",
}
def get_pip_dependencies(): def get_pip_dependencies():
@ -25,7 +34,7 @@ def get_pip_dependencies():
"click >=8.0.4, <9", "click >=8.0.4, <9",
"colorama", "colorama",
"marshmallow == 3.*", "marshmallow == 3.*",
"pyelftools == 0.30", "pyelftools >=0.27, <1",
"pyserial == 3.5.*", # keep in sync "device/monitor/terminal.py" "pyserial == 3.5.*", # keep in sync "device/monitor/terminal.py"
"requests%s == 2.*" % ("[socks]" if is_proxy_set(socks=True) else ""), "requests%s == 2.*" % ("[socks]" if is_proxy_set(socks=True) else ""),
"semantic_version == 2.10.*", "semantic_version == 2.10.*",
@ -35,8 +44,8 @@ def get_pip_dependencies():
home = [ home = [
# PIO Home requirements # PIO Home requirements
"ajsonrpc == 1.2.*", "ajsonrpc == 1.2.*",
"starlette >=0.19, <0.36", "starlette >=0.19, <0.38",
"uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.26"), "uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.30"),
"wsproto == 1.*", "wsproto == 1.*",
] ]
@ -60,12 +69,3 @@ def get_pip_dependencies():
pass pass
return core + home + extra return core + home + extra
def is_proxy_set(socks=False):
for var in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
value = os.getenv(var, os.getenv(var.lower()))
if not value or (socks and not value.startswith("socks5://")):
continue
return True
return False

View File

@ -144,9 +144,9 @@ def list_mdns_services():
if service.properties: if service.properties:
try: try:
properties = { properties = {
k.decode("utf8"): v.decode("utf8") k.decode("utf8"): (
if isinstance(v, bytes) v.decode("utf8") if isinstance(v, bytes) else v
else v )
for k, v in service.properties.items() for k, v in service.properties.items()
} }
json.dumps(properties) json.dumps(properties)

View File

@ -58,7 +58,7 @@ from platformio.project.options import ProjectOptions
"--encoding", "--encoding",
help=( help=(
"Set the encoding for the serial port " "Set the encoding for the serial port "
"(e.g. hexlify, Latin1, UTF-8) [default=%s]" "(e.g. hexlify, Latin-1, UTF-8) [default=%s]"
% ProjectOptions["env.monitor_encoding"].default % ProjectOptions["env.monitor_encoding"].default
), ),
) )
@ -125,9 +125,11 @@ def device_monitor_cmd(**options):
options = apply_project_monitor_options(options, project_options) options = apply_project_monitor_options(options, project_options)
register_filters(platform=platform, options=options) register_filters(platform=platform, options=options)
options["port"] = SerialPortFinder( options["port"] = SerialPortFinder(
board_config=platform.board_config(project_options.get("board")) board_config=(
if platform and project_options.get("board") platform.board_config(project_options.get("board"))
else None, if platform and project_options.get("board")
else None
),
upload_protocol=project_options.get("upload_protocol"), upload_protocol=project_options.get("upload_protocol"),
ensure_ready=True, ensure_ready=True,
).find(initial_port=options["port"]) ).find(initial_port=options["port"])

View File

@ -25,11 +25,12 @@ from platformio.project.config import ProjectConfig
class DeviceMonitorFilterBase(miniterm.Transform): class DeviceMonitorFilterBase(miniterm.Transform):
def __init__(self, options=None): def __init__(self, options=None):
"""Called by PlatformIO to pass context""" """Called by PlatformIO to pass context"""
miniterm.Transform.__init__(self) super().__init__()
self.options = options or {} self.options = options or {}
self.project_dir = self.options.get("project_dir") self.project_dir = self.options.get("project_dir")
self.environment = self.options.get("environment") self.environment = self.options.get("environment")
self._running_terminal = None
self.config = ProjectConfig.get_instance() self.config = ProjectConfig.get_instance()
if not self.environment: if not self.environment:
@ -47,6 +48,12 @@ class DeviceMonitorFilterBase(miniterm.Transform):
def NAME(self): def NAME(self):
raise NotImplementedError("Please declare NAME attribute for the filter class") raise NotImplementedError("Please declare NAME attribute for the filter class")
def set_running_terminal(self, terminal):
self._running_terminal = terminal
def get_running_terminal(self):
return self._running_terminal
def register_filters(platform=None, options=None): def register_filters(platform=None, options=None):
# project filters # project filters

View File

@ -24,12 +24,18 @@ class Hexlify(DeviceMonitorFilterBase):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._counter = 0 self._counter = 0
def set_running_terminal(self, terminal):
# force to Latin-1, issue #4732
if terminal.input_encoding == "UTF-8":
terminal.set_rx_encoding("Latin-1")
super().set_running_terminal(terminal)
def rx(self, text): def rx(self, text):
result = "" result = ""
for b in serial.iterbytes(text): for c in serial.iterbytes(text):
if (self._counter % 16) == 0: if (self._counter % 16) == 0:
result += "\n{:04X} | ".format(self._counter) result += "\n{:04X} | ".format(self._counter)
asciicode = ord(b) asciicode = ord(c)
if asciicode <= 255: if asciicode <= 255:
result += "{:02X} ".format(asciicode) result += "{:02X} ".format(asciicode)
else: else:

View File

@ -110,6 +110,12 @@ def new_terminal(options):
term.raw = options["raw"] term.raw = options["raw"]
term.set_rx_encoding(options["encoding"]) term.set_rx_encoding(options["encoding"])
term.set_tx_encoding(options["encoding"]) term.set_tx_encoding(options["encoding"])
for ts in (term.tx_transformations, term.rx_transformations):
for t in ts:
try:
t.set_running_terminal(term)
except AttributeError:
pass
return term return term

View File

@ -372,15 +372,19 @@ class ProjectRPC(BaseRPCHandler):
return dict( return dict(
platform=dict( platform=dict(
ownername=platform_pkg.metadata.spec.owner ownername=(
if platform_pkg.metadata.spec platform_pkg.metadata.spec.owner
else None, if platform_pkg.metadata.spec
else None
),
name=platform.name, name=platform.name,
title=platform.title, title=platform.title,
version=str(platform_pkg.metadata.version), version=str(platform_pkg.metadata.version),
), ),
board=platform.board_config(board_id).get_brief_data() board=(
if board_id platform.board_config(board_id).get_brief_data()
else None, if board_id
else None
),
frameworks=frameworks or None, frameworks=frameworks or None,
) )

View File

@ -21,8 +21,8 @@ from urllib3.util.retry import Retry
from platformio import __check_internet_hosts__, app, util from platformio import __check_internet_hosts__, app, util
from platformio.cache import ContentCache, cleanup_content_cache from platformio.cache import ContentCache, cleanup_content_cache
from platformio.compat import is_proxy_set
from platformio.exception import PlatformioException, UserSideException from platformio.exception import PlatformioException, UserSideException
from platformio.pipdeps import is_proxy_set
__default_requests_timeout__ = (10, None) # (connect, read) __default_requests_timeout__ = (10, None) # (connect, read)
@ -63,9 +63,11 @@ class HTTPSession(requests.Session):
kwargs["timeout"] = __default_requests_timeout__ kwargs["timeout"] = __default_requests_timeout__
return super().request( return super().request(
method, method,
url (
if url.startswith("http") or not self._x_base_url url
else urljoin(self._x_base_url, url), if url.startswith("http") or not self._x_base_url
else urljoin(self._x_base_url, url)
),
*args, *args,
**kwargs **kwargs
) )

View File

@ -222,9 +222,11 @@ def _install_project_env_libraries(project_env, options):
env_lm = LibraryPackageManager( env_lm = LibraryPackageManager(
os.path.join(config.get("platformio", "libdeps_dir"), project_env), os.path.join(config.get("platformio", "libdeps_dir"), project_env),
compatibility=PackageCompatibility(**compatibility_qualifiers) compatibility=(
if compatibility_qualifiers PackageCompatibility(**compatibility_qualifiers)
else None, if compatibility_qualifiers
else None
),
) )
private_lm = LibraryPackageManager( private_lm = LibraryPackageManager(
os.path.join(config.get("platformio", "lib_dir")) os.path.join(config.get("platformio", "lib_dir"))

View File

@ -86,6 +86,7 @@ def package_publish_cmd( # pylint: disable=too-many-arguments, too-many-locals
package, owner, typex, released_at, private, notify, no_interactive, non_interactive package, owner, typex, released_at, private, notify, no_interactive, non_interactive
): ):
click.secho("Preparing a package...", fg="cyan") click.secho("Preparing a package...", fg="cyan")
package = os.path.abspath(package)
no_interactive = no_interactive or non_interactive no_interactive = no_interactive or non_interactive
owner = owner or AccountClient().get_logged_username() owner = owner or AccountClient().get_logged_username()
do_not_pack = ( do_not_pack = (

View File

@ -65,10 +65,12 @@ def print_search_item(item):
click.echo( click.echo(
"%s%s • Published on %s" "%s%s • Published on %s"
% ( % (
item["type"].capitalize() (
if item["tier"] == "community" item["type"].capitalize()
else click.style( if item["tier"] == "community"
("%s %s" % (item["tier"], item["type"])).title(), bold=True else click.style(
("%s %s" % (item["tier"], item["type"])).title(), bold=True
)
), ),
item["version"]["name"], item["version"]["name"],
util.parse_datetime(item["version"]["released_at"]).strftime("%c"), util.parse_datetime(item["version"]["released_at"]).strftime("%c"),

View File

@ -98,9 +98,13 @@ class PackageManagerInstallMixin:
else: else:
pkg = self.install_from_registry( pkg = self.install_from_registry(
spec, spec,
search_qualifiers=compatibility.to_search_qualifiers() search_qualifiers=(
if compatibility compatibility.to_search_qualifiers(
else None, ["platforms", "frameworks", "authors"]
)
if compatibility
else None
),
) )
if not pkg or not pkg.metadata: if not pkg or not pkg.metadata:

View File

@ -280,11 +280,15 @@ class BasePackageManager( # pylint: disable=too-many-public-methods,too-many-in
# external "URL" mismatch # external "URL" mismatch
if spec.external: if spec.external:
# local folder mismatch # local/symlinked folder mismatch
if os.path.abspath(spec.uri) == os.path.abspath(pkg.path) or ( check_conds = [
os.path.abspath(spec.uri) == os.path.abspath(pkg.path),
spec.uri.startswith("file://") spec.uri.startswith("file://")
and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:]) and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:]),
): spec.uri.startswith("symlink://")
and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[10:]),
]
if any(check_conds):
return True return True
if spec.uri != pkg.metadata.spec.uri: if spec.uri != pkg.metadata.spec.uri:
return False return False

View File

@ -14,7 +14,8 @@
import os import os
from platformio import __core_packages__, exception from platformio import exception
from platformio.dependencies import get_core_dependencies
from platformio.package.exception import UnknownPackageError from platformio.package.exception import UnknownPackageError
from platformio.package.manager.tool import ToolPackageManager from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec from platformio.package.meta import PackageSpec
@ -23,7 +24,7 @@ from platformio.package.meta import PackageSpec
def get_installed_core_packages(): def get_installed_core_packages():
result = [] result = []
pm = ToolPackageManager() pm = ToolPackageManager()
for name, requirements in __core_packages__.items(): for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements) spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
pkg = pm.get_package(spec) pkg = pm.get_package(spec)
if pkg: if pkg:
@ -32,11 +33,11 @@ def get_installed_core_packages():
def get_core_package_dir(name, spec=None, auto_install=True): def get_core_package_dir(name, spec=None, auto_install=True):
if name not in __core_packages__: if name not in get_core_dependencies():
raise exception.PlatformioException("Please upgrade PlatformIO Core") raise exception.PlatformioException("Please upgrade PlatformIO Core")
pm = ToolPackageManager() pm = ToolPackageManager()
spec = spec or PackageSpec( spec = spec or PackageSpec(
owner="platformio", name=name, requirements=__core_packages__[name] owner="platformio", name=name, requirements=get_core_dependencies()[name]
) )
pkg = pm.get_package(spec) pkg = pm.get_package(spec)
if pkg: if pkg:
@ -50,7 +51,7 @@ def get_core_package_dir(name, spec=None, auto_install=True):
def update_core_packages(): def update_core_packages():
pm = ToolPackageManager() pm = ToolPackageManager()
for name, requirements in __core_packages__.items(): for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements) spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
try: try:
pm.update(spec, spec) pm.update(spec, spec)
@ -65,7 +66,7 @@ def remove_unnecessary_core_packages(dry_run=False):
pm = ToolPackageManager() pm = ToolPackageManager()
best_pkg_versions = {} best_pkg_versions = {}
for name, requirements in __core_packages__.items(): for name, requirements in get_core_dependencies().items():
spec = PackageSpec(owner="platformio", name=name, requirements=requirements) spec = PackageSpec(owner="platformio", name=name, requirements=requirements)
pkg = pm.get_package(spec) pkg = pm.get_package(spec)
if not pkg: if not pkg:

View File

@ -294,9 +294,11 @@ class BaseManifestParser:
if not matched_files: if not matched_files:
continue continue
result[root] = dict( result[root] = dict(
name="Examples" name=(
if root == examples_dir "Examples"
else os.path.relpath(root, examples_dir), if root == examples_dir
else os.path.relpath(root, examples_dir)
),
base=os.path.relpath(root, package_dir), base=os.path.relpath(root, package_dir),
files=matched_files, files=matched_files,
) )

View File

@ -276,7 +276,7 @@ class ManifestSchema(BaseSchema):
@staticmethod @staticmethod
@memoized(expire="1h") @memoized(expire="1h")
def load_spdx_licenses(): def load_spdx_licenses():
version = "3.22" version = "3.23"
spdx_data_url = ( spdx_data_url = (
"https://raw.githubusercontent.com/spdx/license-list-data/" "https://raw.githubusercontent.com/spdx/license-list-data/"
f"v{version}/json/licenses.json" f"v{version}/json/licenses.json"

View File

@ -65,7 +65,14 @@ class PackageType:
class PackageCompatibility: class PackageCompatibility:
KNOWN_QUALIFIERS = ("platforms", "frameworks", "authors") KNOWN_QUALIFIERS = (
"owner",
"name",
"version",
"platforms",
"frameworks",
"authors",
)
@classmethod @classmethod
def from_dependency(cls, dependency): def from_dependency(cls, dependency):
@ -89,19 +96,45 @@ class PackageCompatibility:
def __repr__(self): def __repr__(self):
return "PackageCompatibility <%s>" % self.qualifiers return "PackageCompatibility <%s>" % self.qualifiers
def to_search_qualifiers(self): def to_search_qualifiers(self, fields=None):
return self.qualifiers result = {}
for name, value in self.qualifiers.items():
if not fields or name in fields:
result[name] = value
return result
def is_compatible(self, other): def is_compatible(self, other):
assert isinstance(other, PackageCompatibility) assert isinstance(other, PackageCompatibility)
for key, value in self.qualifiers.items(): for key, current_value in self.qualifiers.items():
other_value = other.qualifiers.get(key) other_value = other.qualifiers.get(key)
if not value or not other_value: if not current_value or not other_value:
continue continue
if not items_in_list(value, other_value): if any(isinstance(v, list) for v in (current_value, other_value)):
if not items_in_list(current_value, other_value):
return False
continue
if key == "version":
if not self._compare_versions(current_value, other_value):
return False
continue
if current_value != other_value:
return False return False
return True return True
def _compare_versions(self, current, other):
if current == other:
return True
try:
version = (
other
if isinstance(other, semantic_version.Version)
else cast_version_to_semver(other)
)
return version in semantic_version.SimpleSpec(current)
except ValueError:
pass
return False
class PackageOutdatedResult: class PackageOutdatedResult:
UPDATE_INCREMENT_MAJOR = "major" UPDATE_INCREMENT_MAJOR = "major"
@ -485,9 +518,11 @@ class PackageItem:
def __eq__(self, other): def __eq__(self, other):
conds = [ conds = [
os.path.realpath(self.path) == os.path.realpath(other.path) (
if self.path and other.path os.path.realpath(self.path) == os.path.realpath(other.path)
else self.path == other.path, if self.path and other.path
else self.path == other.path
),
self.metadata == other.metadata, self.metadata == other.metadata,
] ]
return all(conds) return all(conds)

View File

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
import os import os
import sys
from tarfile import open as tarfile_open from tarfile import open as tarfile_open
from time import mktime from time import mktime
from zipfile import ZipFile from zipfile import ZipFile
@ -82,19 +83,23 @@ class TARArchiver(BaseArchiver):
).startswith(base) ).startswith(base)
def extract_item(self, item, dest_dir): def extract_item(self, item, dest_dir):
if sys.version_info >= (3, 12):
self._afo.extract(item, dest_dir, filter="data")
return self.after_extract(item, dest_dir)
# apply custom security logic
dest_dir = self.resolve_path(dest_dir) dest_dir = self.resolve_path(dest_dir)
bad_conds = [ bad_conds = [
self.is_bad_path(item.name, dest_dir), self.is_bad_path(item.name, dest_dir),
self.is_link(item) and self.is_bad_link(item, dest_dir), self.is_link(item) and self.is_bad_link(item, dest_dir),
] ]
if not any(bad_conds): if any(bad_conds):
super().extract_item(item, dest_dir) return click.secho(
else:
click.secho(
"Blocked insecure item `%s` from TAR archive" % item.name, "Blocked insecure item `%s` from TAR archive" % item.name,
fg="red", fg="red",
err=True, err=True,
) )
return super().extract_item(item, dest_dir)
class ZIPArchiver(BaseArchiver): class ZIPArchiver(BaseArchiver):

View File

@ -116,9 +116,9 @@ class PlatformRunMixin:
args, args,
stdout=proc.BuildAsyncPipe( stdout=proc.BuildAsyncPipe(
line_callback=self._on_stdout_line, line_callback=self._on_stdout_line,
data_callback=lambda data: None data_callback=lambda data: (
if self.silent None if self.silent else _write_and_flush(sys.stdout, data)
else _write_and_flush(sys.stdout, data), ),
), ),
stderr=proc.BuildAsyncPipe( stderr=proc.BuildAsyncPipe(
line_callback=self._on_stderr_line, line_callback=self._on_stderr_line,

View File

@ -82,9 +82,11 @@ def lint_configuration(json_output=False):
( (
click.style(error["type"], fg="red"), click.style(error["type"], fg="red"),
error["message"], error["message"],
error.get("source", "") + (f":{error.get('lineno')}") (
if "lineno" in error error.get("source", "") + (f":{error.get('lineno')}")
else "", if "lineno" in error
else ""
),
) )
for error in errors for error in errors
], ],

View File

@ -79,6 +79,7 @@ def project_init_cmd(
env_prefix, env_prefix,
silent, silent,
): ):
project_dir = os.path.abspath(project_dir)
is_new_project = not is_platformio_project(project_dir) is_new_project = not is_platformio_project(project_dir)
if is_new_project: if is_new_project:
if not silent: if not silent:
@ -223,7 +224,7 @@ def init_lib_readme(lib_dir):
This directory is intended for project specific (private) libraries. This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file. PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]"). ("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`: For example, see a structure of the following two libraries `Foo` and `Bar`:

View File

@ -131,27 +131,27 @@ def compute_project_checksum(config):
return checksum.hexdigest() return checksum.hexdigest()
def load_build_metadata(project_dir, env_or_envs, cache=False, debug=False): def load_build_metadata(project_dir, env_or_envs, cache=False, build_type=None):
assert env_or_envs assert env_or_envs
env_names = env_or_envs env_names = env_or_envs
if not isinstance(env_names, list): if not isinstance(env_names, list):
env_names = [env_names] env_names = [env_names]
with fs.cd(project_dir): with fs.cd(project_dir):
result = _get_cached_build_metadata(project_dir, env_names) if cache else {} result = _get_cached_build_metadata(env_names) if cache else {}
# incompatible build-type data # incompatible build-type data
for name in list(result.keys()): for env_name in list(result.keys()):
build_type = result[name].get("build_type", "") if build_type is None:
outdated_conds = [ build_type = ProjectConfig.get_instance().get(
not build_type, f"env:{env_name}", "build_type"
debug and "debug" not in build_type, )
not debug and "debug" in build_type, if result[env_name].get("build_type", "") != build_type:
] del result[env_name]
if any(outdated_conds):
del result[name]
missed_env_names = set(env_names) - set(result.keys()) missed_env_names = set(env_names) - set(result.keys())
if missed_env_names: if missed_env_names:
result.update(_load_build_metadata(project_dir, missed_env_names, debug)) result.update(
_load_build_metadata(project_dir, missed_env_names, build_type)
)
if not isinstance(env_or_envs, list) and env_or_envs in result: if not isinstance(env_or_envs, list) and env_or_envs in result:
return result[env_or_envs] return result[env_or_envs]
@ -162,14 +162,16 @@ def load_build_metadata(project_dir, env_or_envs, cache=False, debug=False):
load_project_ide_data = load_build_metadata load_project_ide_data = load_build_metadata
def _load_build_metadata(project_dir, env_names, debug=False): def _load_build_metadata(project_dir, env_names, build_type=None):
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
from platformio import app from platformio import app
from platformio.run.cli import cli as cmd_run from platformio.run.cli import cli as cmd_run
args = ["--project-dir", project_dir, "--target", "__idedata"] args = ["--project-dir", project_dir, "--target", "__idedata"]
if debug: if build_type == "debug":
args.extend(["--target", "__debug"]) args.extend(["--target", "__debug"])
# if build_type == "test":
# args.extend(["--target", "__test"])
for name in env_names: for name in env_names:
args.extend(["-e", name]) args.extend(["-e", name])
app.set_session_var("pause_telemetry", True) app.set_session_var("pause_telemetry", True)
@ -181,16 +183,16 @@ def _load_build_metadata(project_dir, env_names, debug=False):
raise result.exception raise result.exception
if '"includes":' not in result.output: if '"includes":' not in result.output:
raise exception.UserSideException(result.output) raise exception.UserSideException(result.output)
return _get_cached_build_metadata(project_dir, env_names) return _get_cached_build_metadata(env_names)
def _get_cached_build_metadata(project_dir, env_names): def _get_cached_build_metadata(env_names):
build_dir = ProjectConfig.get_instance( build_dir = ProjectConfig.get_instance().get("platformio", "build_dir")
os.path.join(project_dir, "platformio.ini")
).get("platformio", "build_dir")
result = {} result = {}
for name in env_names: for env_name in env_names:
if not os.path.isfile(os.path.join(build_dir, name, "idedata.json")): if not os.path.isfile(os.path.join(build_dir, env_name, "idedata.json")):
continue continue
result[name] = fs.load_json(os.path.join(build_dir, name, "idedata.json")) result[env_name] = fs.load_json(
os.path.join(build_dir, env_name, "idedata.json")
)
return result return result

View File

@ -91,9 +91,11 @@ class ProjectGenerator:
"default_debug_env_name": get_default_debug_env(self.config), "default_debug_env_name": get_default_debug_env(self.config),
"env_name": self.env_name, "env_name": self.env_name,
"user_home_dir": os.path.abspath(fs.expanduser("~")), "user_home_dir": os.path.abspath(fs.expanduser("~")),
"platformio_path": sys.argv[0] "platformio_path": (
if os.path.isfile(sys.argv[0]) sys.argv[0]
else where_is_program("platformio"), if os.path.isfile(sys.argv[0])
else where_is_program("platformio")
),
"env_path": os.getenv("PATH"), "env_path": os.getenv("PATH"),
"env_pathsep": os.pathsep, "env_pathsep": os.pathsep,
} }

View File

@ -549,7 +549,7 @@ ProjectOptions = OrderedDict(
ConfigEnvOption( ConfigEnvOption(
group="monitor", group="monitor",
name="monitor_encoding", name="monitor_encoding",
description="Custom encoding (e.g. hexlify, Latin1, UTF-8)", description="Custom encoding (e.g. hexlify, Latin-1, UTF-8)",
default="UTF-8", default="UTF-8",
), ),
# Library # Library

View File

@ -41,9 +41,11 @@ def access_list_cmd(owner, urn_type, json_output): # pylint: disable=unused-arg
table_data.append( table_data.append(
( (
"Access:", "Access:",
click.style("Private", fg="red") (
if resource.get("private", False) click.style("Private", fg="red")
else "Public", if resource.get("private", False)
else "Public"
),
) )
) )
table_data.append( table_data.append(

View File

@ -54,9 +54,11 @@ class RegistryFileMirrorIterator:
"head", "head",
self._url_parts.path, self._url_parts.path,
allow_redirects=False, allow_redirects=False,
params=dict(bypass=",".join(self._visited_mirrors)) params=(
if self._visited_mirrors dict(bypass=",".join(self._visited_mirrors))
else None, if self._visited_mirrors
else None
),
x_with_authorization=RegistryClient.allowed_private_packages(), x_with_authorization=RegistryClient.allowed_private_packages(),
) )
stop_conditions = [ stop_conditions = [

View File

@ -123,9 +123,11 @@ class DeviceMonitorClient( # pylint: disable=too-many-instance-attributes
index=i + 1, index=i + 1,
host=device[0] + ":" if len(result) > 1 else "", host=device[0] + ":" if len(result) > 1 else "",
port=device[1]["port"], port=device[1]["port"],
description=device[1]["description"] description=(
if device[1]["description"] != "n/a" device[1]["description"]
else "", if device[1]["description"] != "n/a"
else ""
),
) )
) )
device_index = click.prompt( device_index = click.prompt(

View File

@ -14,6 +14,7 @@
import os import os
import shutil import shutil
import subprocess
import click import click
@ -79,6 +80,7 @@ from platformio.test.runners.factory import TestRunnerFactory
help="A program argument (multiple are allowed)", help="A program argument (multiple are allowed)",
) )
@click.option("--list-tests", is_flag=True) @click.option("--list-tests", is_flag=True)
@click.option("--json-output", is_flag=True)
@click.option("--json-output-path", type=click.Path()) @click.option("--json-output-path", type=click.Path())
@click.option("--junit-output-path", type=click.Path()) @click.option("--junit-output-path", type=click.Path())
@click.option( @click.option(
@ -105,6 +107,7 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
monitor_dtr, monitor_dtr,
program_args, program_args,
list_tests, list_tests,
json_output,
json_output_path, json_output_path,
junit_output_path, junit_output_path,
verbose, verbose,
@ -156,6 +159,7 @@ def cli( # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
stdout_report.generate(verbose=verbose or list_tests) stdout_report.generate(verbose=verbose or list_tests)
for output_format, output_path in [ for output_format, output_path in [
("json", subprocess.STDOUT if json_output else None),
("json", json_output_path), ("json", json_output_path),
("junit", junit_output_path), ("junit", junit_output_path),
]: ]:

View File

@ -15,6 +15,7 @@
import datetime import datetime
import json import json
import os import os
import subprocess
import click import click
@ -24,6 +25,9 @@ from platformio.test.result import TestStatus
class JsonTestReport(TestReportBase): class JsonTestReport(TestReportBase):
def generate(self, output_path, verbose=False): def generate(self, output_path, verbose=False):
if output_path == subprocess.STDOUT:
return click.echo("\n\n" + json.dumps(self.to_json()))
if os.path.isdir(output_path): if os.path.isdir(output_path):
output_path = os.path.join( output_path = os.path.join(
output_path, output_path,
@ -40,6 +44,8 @@ class JsonTestReport(TestReportBase):
if verbose: if verbose:
click.secho(f"Saved JSON report to the {output_path}", fg="green") click.secho(f"Saved JSON report to the {output_path}", fg="green")
return True
def to_json(self): def to_json(self):
result = dict( result = dict(
version="1.0", version="1.0",
@ -62,11 +68,13 @@ class JsonTestReport(TestReportBase):
test_dir=test_suite.test_dir, test_dir=test_suite.test_dir,
status=test_suite.status.name, status=test_suite.status.name,
duration=test_suite.duration, duration=test_suite.duration,
timestamp=datetime.datetime.fromtimestamp(test_suite.timestamp).strftime( timestamp=(
"%Y-%m-%dT%H:%M:%S" datetime.datetime.fromtimestamp(test_suite.timestamp).strftime(
) "%Y-%m-%dT%H:%M:%S"
if test_suite.timestamp )
else None, if test_suite.timestamp
else None
),
testcase_nums=len(test_suite.cases), testcase_nums=len(test_suite.cases),
error_nums=test_suite.get_status_nums(TestStatus.ERRORED), error_nums=test_suite.get_status_nums(TestStatus.ERRORED),
failure_nums=test_suite.get_status_nums(TestStatus.FAILED), failure_nums=test_suite.get_status_nums(TestStatus.FAILED),

View File

@ -184,10 +184,6 @@ void unityOutputComplete(void) { unittest_uart_end(); }
), ),
) )
def __init__(self, *args, **kwargs):
"""Delete when Unity > 2.5.2 is released"""
super().__init__(*args, **kwargs)
def get_unity_framework_config(self): def get_unity_framework_config(self):
if not self.platform.is_embedded(): if not self.platform.is_embedded():
return self.UNITY_FRAMEWORK_CONFIG["native"] return self.UNITY_FRAMEWORK_CONFIG["native"]

View File

@ -23,7 +23,7 @@ from platformio import (
__url__, __url__,
__version__, __version__,
) )
from platformio.pipdeps import get_pip_dependencies from platformio.dependencies import get_pip_dependencies
setup( setup(
name=__title__, name=__title__,

View File

@ -18,7 +18,8 @@ import os
import pytest import pytest
from platformio import __core_packages__, fs from platformio import fs
from platformio.dependencies import get_core_dependencies
from platformio.package.commands.install import package_install_cmd from platformio.package.commands.install import package_install_cmd
from platformio.package.manager.library import LibraryPackageManager from platformio.package.manager.library import LibraryPackageManager
from platformio.package.manager.platform import PlatformPackageManager from platformio.package.manager.platform import PlatformPackageManager
@ -177,7 +178,7 @@ def test_baremetal_project(
), ),
] ]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [ assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]), PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"), PackageSpec("toolchain-atmelavr@1.70300.191015"),
] ]
@ -210,7 +211,7 @@ def test_project(
] ]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [ assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.5.2"), PackageSpec("framework-arduino-avr-attiny@1.5.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]), PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"), PackageSpec("toolchain-atmelavr@1.70300.191015"),
] ]
assert config.get("env:devkit", "lib_deps") == [ assert config.get("env:devkit", "lib_deps") == [

View File

@ -24,7 +24,7 @@ PROJECT_OUTDATED_CONFIG_TPL = """
platform = platformio/atmelavr@^2 platform = platformio/atmelavr@^2
framework = arduino framework = arduino
board = attiny88 board = attiny88
lib_deps = milesburton/DallasTemperature@~3.8.0 lib_deps = milesburton/DallasTemperature@~3.9.0
""" """
PROJECT_UPDATED_CONFIG_TPL = """ PROJECT_UPDATED_CONFIG_TPL = """
@ -32,7 +32,7 @@ PROJECT_UPDATED_CONFIG_TPL = """
platform = platformio/atmelavr@<4 platform = platformio/atmelavr@<4
framework = arduino framework = arduino
board = attiny88 board = attiny88
lib_deps = milesburton/DallasTemperature@^3.8.0 lib_deps = milesburton/DallasTemperature@^3.9.0
""" """
@ -56,7 +56,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
re.MULTILINE, re.MULTILINE,
) )
assert re.search( assert re.search(
r"^DallasTemperature\s+3\.8\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit", r"^DallasTemperature\s+3\.\d\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit",
result.output, result.output,
re.MULTILINE, re.MULTILINE,
) )

View File

@ -16,7 +16,8 @@
import os import os
from platformio import __core_packages__, fs from platformio import fs
from platformio.dependencies import get_core_dependencies
from platformio.package.commands.install import package_install_cmd from platformio.package.commands.install import package_install_cmd
from platformio.package.commands.update import package_update_cmd from platformio.package.commands.update import package_update_cmd
from platformio.package.exception import UnknownPackageError from platformio.package.exception import UnknownPackageError
@ -26,12 +27,14 @@ from platformio.package.manager.tool import ToolPackageManager
from platformio.package.meta import PackageSpec from platformio.package.meta import PackageSpec
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
DALLASTEMPERATURE_LATEST_VERSION = "3.11.0"
PROJECT_OUTDATED_CONFIG_TPL = """ PROJECT_OUTDATED_CONFIG_TPL = """
[env:devkit] [env:devkit]
platform = platformio/atmelavr@^2 platform = platformio/atmelavr@^2
framework = arduino framework = arduino
board = attiny88 board = attiny88
lib_deps = milesburton/DallasTemperature@~3.8.0 lib_deps = milesburton/DallasTemperature@^3.8.0
""" """
PROJECT_UPDATED_CONFIG_TPL = """ PROJECT_UPDATED_CONFIG_TPL = """
@ -162,7 +165,7 @@ def test_project(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit") os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
) )
assert pkgs_to_specs(lm.get_installed()) == [ assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"), PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"),
PackageSpec( PackageSpec(
"OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire") "OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire")
), ),
@ -172,11 +175,11 @@ def test_project(
] ]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [ assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"), PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]), PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.50400.190710"), PackageSpec("toolchain-atmelavr@1.50400.190710"),
] ]
assert config.get("env:devkit", "lib_deps") == [ assert config.get("env:devkit", "lib_deps") == [
"milesburton/DallasTemperature@~3.8.0" "milesburton/DallasTemperature@^3.8.0"
] ]
# update packages # update packages
@ -202,7 +205,7 @@ def test_project(
] ]
assert pkgs_to_specs(ToolPackageManager().get_installed()) == [ assert pkgs_to_specs(ToolPackageManager().get_installed()) == [
PackageSpec("framework-arduino-avr-attiny@1.3.2"), PackageSpec("framework-arduino-avr-attiny@1.3.2"),
PackageSpec("tool-scons@%s" % __core_packages__["tool-scons"][1:]), PackageSpec("tool-scons@%s" % get_core_dependencies()["tool-scons"][1:]),
PackageSpec("toolchain-atmelavr@1.70300.191015"), PackageSpec("toolchain-atmelavr@1.70300.191015"),
PackageSpec("toolchain-atmelavr@1.50400.190710"), PackageSpec("toolchain-atmelavr@1.50400.190710"),
] ]
@ -227,7 +230,7 @@ def test_custom_project_libraries(
project_dir = tmp_path / "project" project_dir = tmp_path / "project"
project_dir.mkdir() project_dir.mkdir()
(project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL) (project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL)
spec = "milesburton/DallasTemperature@~3.8.0" spec = "milesburton/DallasTemperature@^3.8.0"
result = clirunner.invoke( result = clirunner.invoke(
package_install_cmd, package_install_cmd,
["-d", str(project_dir), "-e", "devkit", "-l", spec], ["-d", str(project_dir), "-e", "devkit", "-l", spec],
@ -240,7 +243,7 @@ def test_custom_project_libraries(
os.path.join(config.get("platformio", "libdeps_dir"), "devkit") os.path.join(config.get("platformio", "libdeps_dir"), "devkit")
) )
assert pkgs_to_specs(lm.get_installed()) == [ assert pkgs_to_specs(lm.get_installed()) == [
PackageSpec("DallasTemperature@3.8.1"), PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"),
PackageSpec( PackageSpec(
"OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire") "OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire")
), ),

View File

@ -803,3 +803,49 @@ check_src_filters =
assert errors + warnings + style == EXPECTED_DEFECTS assert errors + warnings + style == EXPECTED_DEFECTS
assert "test.cpp" in result.output assert "test.cpp" in result.output
assert "main.cpp" not in result.output assert "main.cpp" not in result.output
def test_check_sources_in_project_root(clirunner, validate_cliresult, tmpdir_factory):
tmpdir = tmpdir_factory.mktemp("project")
config = (
"""
[platformio]
src_dir = ./
"""
+ DEFAULT_CONFIG
)
tmpdir.join("platformio.ini").write(config)
tmpdir.join("main.cpp").write(TEST_CODE)
tmpdir.mkdir("spi").join("uart.cpp").write(TEST_CODE)
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
validate_cliresult(result)
errors, warnings, style = count_defects(result.output)
assert result.exit_code == 0
assert errors + warnings + style == EXPECTED_DEFECTS * 2
def test_check_sources_in_external_dir(clirunner, validate_cliresult, tmpdir_factory):
tmpdir = tmpdir_factory.mktemp("project")
external_src_dir = tmpdir_factory.mktemp("external_src_dir")
config = (
f"""
[platformio]
src_dir = {external_src_dir}
"""
+ DEFAULT_CONFIG
)
tmpdir.join("platformio.ini").write(config)
external_src_dir.join("main.cpp").write(TEST_CODE)
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
validate_cliresult(result)
errors, warnings, style = count_defects(result.output)
assert result.exit_code == 0
assert errors + warnings + style == EXPECTED_DEFECTS

View File

@ -15,6 +15,7 @@
import json import json
import os import os
from platformio import fs
from platformio.commands.boards import cli as cmd_boards from platformio.commands.boards import cli as cmd_boards
from platformio.project.commands.init import project_init_cmd from platformio.project.commands.init import project_init_cmd
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
@ -36,27 +37,28 @@ def test_init_default(clirunner, validate_cliresult):
validate_pioproject(os.getcwd()) validate_pioproject(os.getcwd())
def test_init_ext_folder(clirunner, validate_cliresult):
with clirunner.isolated_filesystem():
ext_folder_name = "ext_folder"
os.makedirs(ext_folder_name)
result = clirunner.invoke(project_init_cmd, ["-d", ext_folder_name])
validate_cliresult(result)
validate_pioproject(os.path.join(os.getcwd(), ext_folder_name))
def test_init_duplicated_boards(clirunner, validate_cliresult, tmpdir): def test_init_duplicated_boards(clirunner, validate_cliresult, tmpdir):
with tmpdir.as_cwd(): project_dir = str(tmpdir.join("ext_folder"))
for _ in range(2): os.makedirs(project_dir)
result = clirunner.invoke(
project_init_cmd, with fs.cd(os.path.dirname(project_dir)):
["-b", "uno", "-b", "uno", "--no-install-dependencies"], result = clirunner.invoke(
) project_init_cmd,
validate_cliresult(result) [
validate_pioproject(str(tmpdir)) "-d",
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini")) os.path.basename(project_dir),
config.validate() "-b",
assert set(config.sections()) == set(["env:uno"]) "uno",
"-b",
"uno",
"--no-install-dependencies",
],
)
validate_cliresult(result)
validate_pioproject(project_dir)
config = ProjectConfig(os.path.join(project_dir, "platformio.ini"))
config.validate()
assert set(config.sections()) == set(["env:uno"])
def test_init_ide_without_board(clirunner, tmpdir): def test_init_ide_without_board(clirunner, tmpdir):

View File

@ -42,7 +42,7 @@ board = devkit
framework = foo framework = foo
lib_deps = lib_deps =
CustomLib CustomLib
ArduinoJson @ 5.10.1 ArduinoJson @ 6.18.5
""" """
) )
result = clirunner.invoke( result = clirunner.invoke(
@ -163,7 +163,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
storage_dir = tmpdir_factory.mktemp("test-updates") storage_dir = tmpdir_factory.mktemp("test-updates")
result = clirunner.invoke( result = clirunner.invoke(
cmd_lib, cmd_lib,
["-d", str(storage_dir), "install", "ArduinoJson @ 5.10.1", "Blynk @ ~0.5.0"], ["-d", str(storage_dir), "install", "ArduinoJson @ 6.18.5", "Blynk @ ~1.2"],
) )
validate_cliresult(result) validate_cliresult(result)
result = clirunner.invoke( result = clirunner.invoke(
@ -173,17 +173,17 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
outdated = json.loads(result.stdout) outdated = json.loads(result.stdout)
assert len(outdated) == 2 assert len(outdated) == 2
# ArduinoJson # ArduinoJson
assert outdated[0]["version"] == "5.10.1" assert outdated[0]["version"] == "6.18.5"
assert outdated[0]["versionWanted"] is None assert outdated[0]["versionWanted"] is None
assert semantic_version.Version( assert semantic_version.Version(
outdated[0]["versionLatest"] outdated[0]["versionLatest"]
) > semantic_version.Version("6.16.0") ) > semantic_version.Version("6.18.5")
# Blynk # Blynk
assert outdated[1]["version"] == "0.5.4" assert outdated[1]["version"] == "1.2.0"
assert outdated[1]["versionWanted"] is None assert outdated[1]["versionWanted"] is None
assert semantic_version.Version( assert semantic_version.Version(
outdated[1]["versionLatest"] outdated[1]["versionLatest"]
) > semantic_version.Version("0.6.0") ) > semantic_version.Version("1.2.0")
# check with spec # check with spec
result = clirunner.invoke( result = clirunner.invoke(
@ -194,19 +194,19 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
"update", "update",
"--dry-run", "--dry-run",
"--json-output", "--json-output",
"ArduinoJson @ ^5", "ArduinoJson @ ^6",
], ],
) )
validate_cliresult(result) validate_cliresult(result)
outdated = json.loads(result.stdout) outdated = json.loads(result.stdout)
assert outdated[0]["version"] == "5.10.1" assert outdated[0]["version"] == "6.18.5"
assert outdated[0]["versionWanted"] == "5.13.4" assert outdated[0]["versionWanted"] == "6.21.5"
assert semantic_version.Version( assert semantic_version.Version(
outdated[0]["versionLatest"] outdated[0]["versionLatest"]
) > semantic_version.Version("6.16.0") ) > semantic_version.Version("6.16.0")
# update with spec # update with spec
result = clirunner.invoke( result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^5.10.1"] cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^6.18.5"]
) )
validate_cliresult(result) validate_cliresult(result)
result = clirunner.invoke( result = clirunner.invoke(
@ -215,12 +215,12 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
validate_cliresult(result) validate_cliresult(result)
items = json.loads(result.stdout) items = json.loads(result.stdout)
assert len(items) == 2 assert len(items) == 2
assert items[0]["version"] == "5.13.4" assert items[0]["version"] == "6.21.5"
assert items[1]["version"] == "0.5.4" assert items[1]["version"] == "1.2.0"
# Check incompatible # Check incompatible
result = clirunner.invoke( result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^5"] cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^6"]
) )
with pytest.raises( with pytest.raises(
AssertionError, AssertionError,
@ -228,7 +228,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory
): ):
validate_cliresult(result) validate_cliresult(result)
result = clirunner.invoke( result = clirunner.invoke(
cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^5"] cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^6"]
) )
validate_cliresult(result) validate_cliresult(result)
assert "ArduinoJson@5.13.4 is already up-to-date" in result.stdout assert "ArduinoJson@6.21.5 is already up-to-date" in result.stdout

View File

@ -23,6 +23,7 @@ from platformio.package.exception import UnknownPackageError
from platformio.util import strip_ansi_codes from platformio.util import strip_ansi_codes
PlatformioCLI.leftover_args = ["--json-output"] # hook for click PlatformioCLI.leftover_args = ["--json-output"] # hook for click
ARDUINO_JSON_VERSION = "6.21.5"
def test_search(clirunner, validate_cliresult): def test_search(clirunner, validate_cliresult):
@ -44,10 +45,10 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor
"-g", "-g",
"install", "install",
"64", "64",
"ArduinoJson@~5.10.0", "ArduinoJson@~6",
"547@2.2.4", "547@2.7.3",
"AsyncMqttClient@<=0.8.2", "AsyncMqttClient@<=0.8.2",
"Adafruit PN532@1.2.0", "Adafruit PN532@1.3.2",
], ],
) )
validate_cliresult(result) validate_cliresult(result)
@ -60,7 +61,7 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor
items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()] items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()]
items2 = [ items2 = [
"ArduinoJson", "ArduinoJson",
"ArduinoJson@5.10.1", f"ArduinoJson@{ARDUINO_JSON_VERSION}",
"NeoPixelBus", "NeoPixelBus",
"AsyncMqttClient", "AsyncMqttClient",
"ESPAsyncTCP", "ESPAsyncTCP",
@ -79,7 +80,7 @@ def test_global_install_archive(clirunner, validate_cliresult, isolated_pio_core
"install", "install",
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip", "https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip",
"https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip@5.8.2", "https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip@5.8.2",
"SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz", "SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz",
"https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip", "https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip",
], ],
) )
@ -142,7 +143,7 @@ def test_install_duplicates( # pylint: disable=unused-argument
[ [
"-g", "-g",
"install", "install",
"https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz", "https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz",
], ],
) )
validate_cliresult(result) validate_cliresult(result)
@ -176,11 +177,11 @@ def test_global_lib_list(clirunner, validate_cliresult):
n in result.output n in result.output
for n in ( for n in (
"required: https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip", "required: https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip",
"ArduinoJson @ 5.10.1", f"ArduinoJson @ {ARDUINO_JSON_VERSION}",
"required: git+https://github.com/gioblu/PJON.git#3.0", "required: git+https://github.com/gioblu/PJON.git#3.0",
"PJON @ 3.0.0+sha.1fb26f", "PJON @ 3.0.0+sha.1fb26f",
) )
) ), result.output
result = clirunner.invoke(cmd_lib, ["-g", "list", "--json-output"]) result = clirunner.invoke(cmd_lib, ["-g", "list", "--json-output"])
assert all( assert all(
@ -188,7 +189,7 @@ def test_global_lib_list(clirunner, validate_cliresult):
for n in ( for n in (
"__pkg_dir", "__pkg_dir",
'"__src_url": "git+https://github.com/gioblu/PJON.git#6.2"', '"__src_url": "git+https://github.com/gioblu/PJON.git#6.2"',
'"version": "5.10.1"', f'"version": "{ARDUINO_JSON_VERSION}"',
) )
) )
items1 = [i["name"] for i in json.loads(result.output)] items1 = [i["name"] for i in json.loads(result.output)]
@ -218,13 +219,13 @@ def test_global_lib_list(clirunner, validate_cliresult):
] ]
versions2 = [ versions2 = [
"ArduinoJson@5.8.2", "ArduinoJson@5.8.2",
"ArduinoJson@5.10.1", f"ArduinoJson@{ARDUINO_JSON_VERSION}",
"AsyncMqttClient@0.8.2", "AsyncMqttClient@0.8.2",
"NeoPixelBus@2.2.4", "NeoPixelBus@2.7.3",
"PJON@6.2.0+sha.07fe9aa", "PJON@6.2.0+sha.07fe9aa",
"PJON@3.0.0+sha.1fb26fd", "PJON@3.0.0+sha.1fb26fd",
"PubSubClient@2.6.0+sha.bef5814", "PubSubClient@2.6.0+sha.bef5814",
"Adafruit PN532@1.2.0", "Adafruit PN532@1.3.2",
] ]
assert set(versions1) >= set(versions2) assert set(versions1) >= set(versions2)
@ -249,7 +250,7 @@ def test_global_lib_update(clirunner, validate_cliresult):
assert "__pkg_dir" in oudated[0] assert "__pkg_dir" in oudated[0]
result = clirunner.invoke(cmd_lib, ["-g", "update", oudated[0]["__pkg_dir"]]) result = clirunner.invoke(cmd_lib, ["-g", "update", oudated[0]["__pkg_dir"]])
validate_cliresult(result) validate_cliresult(result)
assert "Removing NeoPixelBus @ 2.2.4" in strip_ansi_codes(result.output) assert "Removing NeoPixelBus @ 2.7.3" in strip_ansi_codes(result.output)
# update all libraries # update all libraries
result = clirunner.invoke( result = clirunner.invoke(

View File

@ -63,7 +63,7 @@ def test_install_unknown_from_registry(clirunner):
def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio_core): def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke( result = clirunner.invoke(
cli_platform.platform_install, cli_platform.platform_install,
["atmelavr@1.2.0", "--skip-default-package"], ["atmelavr@2.2.0", "--skip-default-package"],
) )
assert result.exit_code == 0 assert result.exit_code == 0
@ -71,11 +71,11 @@ def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio
def test_install_known_version(clirunner, validate_cliresult, isolated_pio_core): def test_install_known_version(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke( result = clirunner.invoke(
cli_platform.platform_install, cli_platform.platform_install,
["atmelavr@2.0.0", "--skip-default-package", "--with-package", "tool-avrdude"], ["atmelavr@4.2.0", "--skip-default-package", "--with-package", "tool-avrdude"],
) )
validate_cliresult(result) validate_cliresult(result)
output = strip_ansi_codes(result.output) output = strip_ansi_codes(result.output)
assert "atmelavr @ 2.0.0" in output assert "atmelavr@4.2.0" in output
assert not os.path.isdir(str(isolated_pio_core.join("packages"))) assert not os.path.isdir(str(isolated_pio_core.join("packages")))
@ -128,14 +128,14 @@ def test_update_raw(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke(cli_platform.platform_update, ["atmelavr"]) result = clirunner.invoke(cli_platform.platform_update, ["atmelavr"])
validate_cliresult(result) validate_cliresult(result)
output = strip_ansi_codes(result.output) output = strip_ansi_codes(result.output)
assert "Removing atmelavr @ 2.0.0" in output assert "Removing atmelavr @ 4.2.0" in output
assert "Platform Manager: Installing platformio/atmelavr @" in output assert "Platform Manager: Installing platformio/atmelavr @" in output
assert len(isolated_pio_core.join("packages").listdir()) == 2 assert len(isolated_pio_core.join("packages").listdir()) == 2
def test_uninstall(clirunner, validate_cliresult, isolated_pio_core): def test_uninstall(clirunner, validate_cliresult, isolated_pio_core):
result = clirunner.invoke( result = clirunner.invoke(
cli_platform.platform_uninstall, ["atmelavr@1.2.0", "atmelavr", "espressif8266"] cli_platform.platform_uninstall, ["atmelavr@2.2.0", "atmelavr", "espressif8266"]
) )
validate_cliresult(result) validate_cliresult(result)
assert not isolated_pio_core.join("platforms").listdir() assert not isolated_pio_core.join("platforms").listdir()

View File

@ -219,7 +219,7 @@ def test_install_from_registry(isolated_pio_core, tmpdir_factory):
# test conflicted names # test conflicted names
lm = LibraryPackageManager(str(tmpdir_factory.mktemp("conflicted-storage"))) lm = LibraryPackageManager(str(tmpdir_factory.mktemp("conflicted-storage")))
lm.set_log_level(logging.ERROR) lm.set_log_level(logging.ERROR)
lm.install("z3t0/IRremote@2.6.1") lm.install("z3t0/IRremote")
lm.install("mbed-yuhki50/IRremote") lm.install("mbed-yuhki50/IRremote")
assert len(lm.get_installed()) == 2 assert len(lm.get_installed()) == 2
@ -554,14 +554,14 @@ def test_uninstall(isolated_pio_core, tmpdir_factory):
assert not lm.get_installed() assert not lm.get_installed()
# test uninstall dependencies # test uninstall dependencies
assert lm.install("AsyncMqttClient-esphome @ 0.8.4") assert lm.install("AsyncMqttClient-esphome")
assert len(lm.get_installed()) == 3 assert len(lm.get_installed()) == 3
assert lm.uninstall("AsyncMqttClient-esphome", skip_dependencies=True) assert lm.uninstall("AsyncMqttClient-esphome", skip_dependencies=True)
assert len(lm.get_installed()) == 2 assert len(lm.get_installed()) == 2
lm = LibraryPackageManager(str(storage_dir)) lm = LibraryPackageManager(str(storage_dir))
lm.set_log_level(logging.ERROR) lm.set_log_level(logging.ERROR)
assert lm.install("AsyncMqttClient-esphome @ 0.8.4") assert lm.install("AsyncMqttClient-esphome")
assert lm.uninstall("AsyncMqttClient-esphome") assert lm.uninstall("AsyncMqttClient-esphome")
assert not lm.get_installed() assert not lm.get_installed()
@ -604,23 +604,23 @@ def test_update_with_metadata(isolated_pio_core, tmpdir_factory):
assert str(outdated.current) == "1.8.7" assert str(outdated.current) == "1.8.7"
assert outdated.latest > semantic_version.Version("1.10.0") assert outdated.latest > semantic_version.Version("1.10.0")
pkg = lm.install("ArduinoJson @ 5.10.1") pkg = lm.install("ArduinoJson @ 6.19.4")
# test latest # test latest
outdated = lm.outdated(pkg) outdated = lm.outdated(pkg)
assert str(outdated.current) == "5.10.1" assert str(outdated.current) == "6.19.4"
assert outdated.wanted is None assert outdated.wanted is None
assert outdated.latest > outdated.current assert outdated.latest > outdated.current
assert outdated.latest > semantic_version.Version("5.99.99") assert outdated.latest > semantic_version.Version("5.99.99")
# test wanted # test wanted
outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~5")) outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~6"))
assert str(outdated.current) == "5.10.1" assert str(outdated.current) == "6.19.4"
assert str(outdated.wanted) == "5.13.4" assert str(outdated.wanted) == "6.21.5"
assert outdated.latest > semantic_version.Version("6.16.0") assert outdated.latest > semantic_version.Version("6.16.0")
# update to the wanted 5.x # update to the wanted 6.x
new_pkg = lm.update("ArduinoJson@^5", PackageSpec("ArduinoJson@^5")) new_pkg = lm.update("ArduinoJson@^6", PackageSpec("ArduinoJson@^6"))
assert str(new_pkg.metadata.version) == "5.13.4" assert str(new_pkg.metadata.version) == "6.21.5"
# check that old version is removed # check that old version is removed
assert len(lm.get_installed()) == 2 assert len(lm.get_installed()) == 2