From eb5905b719bef4f02afddcb79065dd1791489b8b Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Tue, 31 Mar 2026 15:42:09 -0700 Subject: [PATCH 1/9] fix to handle py3 and py2+py3 packages --- src/volttron/server/aip.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/volttron/server/aip.py b/src/volttron/server/aip.py index b17cdc57c..afc28331f 100644 --- a/src/volttron/server/aip.py +++ b/src/volttron/server/aip.py @@ -764,7 +764,14 @@ def _construct_package_name_from_wheel(agent_wheel): agent_name = agent_wheel if agent_wheel.endswith(".whl"): wheel = agent_wheel.split("/")[-1] - agent_name_with_version = wheel.replace("-py3-none-any.whl", "").replace("_", "-") + # handle both py3 and python2&3 compatible wheels + stripped = re.sub(r'-(py2\.py3|py3)-[^-]+-[^-]+\.whl$', '', wheel) + if stripped == wheel: + raise RuntimeError( + f"Unable to parse package name from wheel filename '{wheel}'. " + f"Expected format: {{name}}-{{version}}-(py3|py2.py3)-{{abi}}-{{platform}}.whl" + ) + agent_name_with_version = stripped.replace("_", "-") agent_name = agent_name_with_version[:agent_name_with_version.rfind("-")] return agent_name From 7bdf178843e1b6cda2e56250cbb9621b6951a82d Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Tue, 31 Mar 2026 15:43:13 -0700 Subject: [PATCH 2/9] added vctl remove-lib --- src/volttron/client/commands/control.py | 21 ++++++++++++------- src/volttron/server/aip.py | 9 +++++++- .../services/control/control_service.py | 4 ++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/volttron/client/commands/control.py b/src/volttron/client/commands/control.py index 3a2ed1376..70cf03f6a 100644 --- a/src/volttron/client/commands/control.py +++ b/src/volttron/client/commands/control.py @@ -273,6 +273,8 @@ def remove_agent(opts, remove_auth=True): _stdout.write("Removing {} {}\n".format(agent.uuid, agent.name)) opts.connection.call("remove_agent", agent.uuid, remove_auth=remove_auth) +def remove_lib(opts): + opts.connection.call("remove_library", opts.library) def _calc_min_uuid_length(agents: list[AgentMeta]): n = 0 @@ -2138,7 +2140,7 @@ def main(): os.environ["VOLTTRON_HOME"] = volttron_home global_args = config.ArgumentParser(description="global options", add_help=False) - + # Connection options connection_group = global_args.add_argument_group('Connection Options') connection_group.add_argument( @@ -2154,7 +2156,7 @@ def main(): timeout=120.0, help="timeout in seconds for remote calls (default: %(default)g)", ) - + # Debug options debug_group = global_args.add_argument_group('Debug Options') debug_group.add_argument( @@ -2164,7 +2166,7 @@ def main(): ) filterable = config.ArgumentParser(add_help=False) - + # Filter/Search options filter_group = filterable.add_argument_group('Filter Options') filter_group.add_argument( @@ -2198,7 +2200,7 @@ def main(): argument_default=argparse.SUPPRESS, parents=[global_args], ) - + # Output formatting options output_group = parser.add_argument_group('Output Options') output_group.add_argument( @@ -2207,7 +2209,7 @@ def main(): default=False, help="format output to json", ) - + # Logging options logging_group = parser.add_argument_group('Logging Options') logging_group.add_argument( @@ -2246,7 +2248,7 @@ def main(): default=logging.WARNING, help="set logger verboseness", ) - + # Hidden options parser.add_argument("--show-config", action="store_true", help=argparse.SUPPRESS) @@ -2269,7 +2271,7 @@ def add_parser(*args, **kwargs) -> argparse.ArgumentParser: kwargs["parents"] = parents subparser = kwargs.pop("subparser", top_level_subparsers) return subparser.add_parser(*args, **kwargs) - + add_publish_parser(add_parser) add_subscribe_parser(add_parser) add_install_agent_parser(add_parser) @@ -2296,6 +2298,11 @@ def add_parser(*args, **kwargs) -> argparse.ArgumentParser: ) remove.set_defaults(func=remove_agent, force=False) + remove_lib_parser = add_parser("remove-lib", help="remove library") + remove_lib_parser.add_argument("library", help="name of the library") + remove_lib_parser.set_defaults(func=remove_lib) + + peers = add_parser("peerlist", help="list the peers connected to the platform") peers.set_defaults(func=list_peers) diff --git a/src/volttron/server/aip.py b/src/volttron/server/aip.py index afc28331f..c6b5d7cc8 100644 --- a/src/volttron/server/aip.py +++ b/src/volttron/server/aip.py @@ -588,7 +588,14 @@ def install_library(self, library, force=False, pre_release=False): """ name, name_with_version, _ = self.install_agent_or_lib_source(library, force, pre_release) return name_with_version - + + def remove_library(self, library): + """ + Removes the library from current pyproject toml project, which in turn uninstalls the library from current + venv + """ + cmd = ["poetry", "--directory", self._server_opts.poetry_project_path.as_posix(), "remove", library] + execute_command(cmd) def _raise_error_if_identity_exists_without_force(self, vip_identity: str, force: bool): """ diff --git a/src/volttron/services/control/control_service.py b/src/volttron/services/control/control_service.py index 3ec480de2..8588b1c47 100644 --- a/src/volttron/services/control/control_service.py +++ b/src/volttron/services/control/control_service.py @@ -528,6 +528,10 @@ def install_library(self, source, data, force, allow_prerelease) -> str: force=force, pre_release=allow_prerelease) + @RPC.export + def remove_library(self, name): + self._aip.remove_library(name) + def _raise_error_if_identity_exists_without_force(self, vip_identity: str, force: bool) -> Identity: """ This will raise a ValueError if the identity passed exists but From 03faf2bbc6ce43d6a8ae4aafcc4ede995c7d1ec9 Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Tue, 31 Mar 2026 16:16:17 -0700 Subject: [PATCH 3/9] using tomli instead of toml for pyproject.toml parsing --- pyproject.toml | 2 -- src/volttron/utils/version.py | 9 ++++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bf0a99fa1..288ec9b84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ poetry = "^2.0" python = "^3.10" gevent = ">=24.2.1,<24.3" PyYAML = "^6.0" -toml = "^0.10.2" dateutils = "^0.6.12" tzlocal = "^4.1" psutil = "^5.9.0" @@ -53,7 +52,6 @@ cattrs = "^23.2.3" pytest = "^8.3.3" pre-commit = "^4.0.1" yapf = "^0.32.0" -toml = "^0.10.2" mypy = "^1.2.0" pytest-timeout = "^2.3.1" pytest-mock = "^3.10.0" diff --git a/src/volttron/utils/version.py b/src/volttron/utils/version.py index a5a20474a..f63d8fa08 100644 --- a/src/volttron/utils/version.py +++ b/src/volttron/utils/version.py @@ -45,15 +45,14 @@ try: # We should be in a develop environment therefore # we can get the version from the toml pyproject.toml - root = Path(__file__).parent.parent.parent + root = Path(__file__).parent.parent.parent.parent tomle_file = root.joinpath("pyproject.toml") if not tomle_file.exists(): raise ValueError( f"Couldn't find pyproject.toml file for finding version. ({str(tomle_file)})") - import toml - - pyproject = toml.load(tomle_file) - + import tomli + with open(tomle_file, "rb") as f: + pyproject = tomli.load(f) __version__ = pyproject["tool"]["poetry"]["version"] except ValueError: cmd = [sys.executable, "-m", "pip", "list"] From 02a857a7da3acc517668fde865de8d5e2502e33b Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Wed, 1 Apr 2026 14:38:36 -0700 Subject: [PATCH 4/9] changes based on copilot review --- pyproject.toml | 1 + src/volttron/utils/version.py | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 288ec9b84..bf753d8ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ blinker = "^1.7.0" dataclass-wizard = "^0.22.3" returns = "^0.22.0" cattrs = "^23.2.3" +tomli = { version = "^2.0.1", python = "<3.11" } [tool.poetry.group.dev.dependencies] pytest = "^8.3.3" diff --git a/src/volttron/utils/version.py b/src/volttron/utils/version.py index f63d8fa08..98516d1cf 100644 --- a/src/volttron/utils/version.py +++ b/src/volttron/utils/version.py @@ -42,16 +42,17 @@ __version__ = importlib_metadata.version('volttron-core') except importlib_metadata.PackageNotFoundError: + # TODO - use tomlib once we migrate to py 3.11 or above try: # We should be in a develop environment therefore # we can get the version from the toml pyproject.toml root = Path(__file__).parent.parent.parent.parent - tomle_file = root.joinpath("pyproject.toml") - if not tomle_file.exists(): + toml_file = root.joinpath("pyproject.toml") + if not toml_file.exists(): raise ValueError( - f"Couldn't find pyproject.toml file for finding version. ({str(tomle_file)})") + f"Couldn't find pyproject.toml file for finding version. ({str(toml_file)})") import tomli - with open(tomle_file, "rb") as f: + with open(toml_file, "rb") as f: pyproject = tomli.load(f) __version__ = pyproject["tool"]["poetry"]["version"] except ValueError: From 8d08cafb0bbf6e52640df6ca497b330cea8ec9e8 Mon Sep 17 00:00:00 2001 From: Chandrika Date: Wed, 1 Apr 2026 14:46:45 -0700 Subject: [PATCH 5/9] Update src/volttron/client/commands/control.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/volttron/client/commands/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/volttron/client/commands/control.py b/src/volttron/client/commands/control.py index 70cf03f6a..d660a2b45 100644 --- a/src/volttron/client/commands/control.py +++ b/src/volttron/client/commands/control.py @@ -273,9 +273,9 @@ def remove_agent(opts, remove_auth=True): _stdout.write("Removing {} {}\n".format(agent.uuid, agent.name)) opts.connection.call("remove_agent", agent.uuid, remove_auth=remove_auth) + def remove_lib(opts): opts.connection.call("remove_library", opts.library) - def _calc_min_uuid_length(agents: list[AgentMeta]): n = 0 for agent1 in agents: From 88a6078bb12a855844fcc468bf7e3a3c858afdb8 Mon Sep 17 00:00:00 2001 From: Chandrika Date: Wed, 1 Apr 2026 14:47:37 -0700 Subject: [PATCH 6/9] Update src/volttron/utils/version.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/volttron/utils/version.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/volttron/utils/version.py b/src/volttron/utils/version.py index 98516d1cf..3931c5f3a 100644 --- a/src/volttron/utils/version.py +++ b/src/volttron/utils/version.py @@ -51,9 +51,19 @@ if not toml_file.exists(): raise ValueError( f"Couldn't find pyproject.toml file for finding version. ({str(toml_file)})") - import tomli - with open(toml_file, "rb") as f: - pyproject = tomli.load(f) + + # Prefer stdlib tomllib when available (Python 3.11+), fall back to tomli if installed. + try: + import tomllib as toml_loader # type: ignore[attr-defined] + except ModuleNotFoundError: + try: + import tomli as toml_loader # type: ignore[import] + except ModuleNotFoundError: + # Neither tomllib nor tomli is available; fall back to pip list mechanism. + raise ValueError("No TOML parser available") + + with open(tomle_file, "rb") as f: + pyproject = toml_loader.load(f) __version__ = pyproject["tool"]["poetry"]["version"] except ValueError: cmd = [sys.executable, "-m", "pip", "list"] From 065cdd2bf11911e5aa252164a5f0b19c4a6d3ae9 Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Wed, 1 Apr 2026 14:54:39 -0700 Subject: [PATCH 7/9] minor var name fix --- src/volttron/utils/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volttron/utils/version.py b/src/volttron/utils/version.py index 3931c5f3a..12bb28e99 100644 --- a/src/volttron/utils/version.py +++ b/src/volttron/utils/version.py @@ -42,7 +42,6 @@ __version__ = importlib_metadata.version('volttron-core') except importlib_metadata.PackageNotFoundError: - # TODO - use tomlib once we migrate to py 3.11 or above try: # We should be in a develop environment therefore # we can get the version from the toml pyproject.toml @@ -54,6 +53,7 @@ # Prefer stdlib tomllib when available (Python 3.11+), fall back to tomli if installed. try: + # works if python3.11+ import tomllib as toml_loader # type: ignore[attr-defined] except ModuleNotFoundError: try: @@ -62,7 +62,7 @@ # Neither tomllib nor tomli is available; fall back to pip list mechanism. raise ValueError("No TOML parser available") - with open(tomle_file, "rb") as f: + with open(toml_file, "rb") as f: pyproject = toml_loader.load(f) __version__ = pyproject["tool"]["poetry"]["version"] except ValueError: From 5fbd268a01582828cc6b8ffa7ed966b868715213 Mon Sep 17 00:00:00 2001 From: Chandrika Date: Wed, 1 Apr 2026 15:02:54 -0700 Subject: [PATCH 8/9] Update src/volttron/services/control/control_service.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/volttron/services/control/control_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volttron/services/control/control_service.py b/src/volttron/services/control/control_service.py index 8588b1c47..6f342a68b 100644 --- a/src/volttron/services/control/control_service.py +++ b/src/volttron/services/control/control_service.py @@ -529,8 +529,8 @@ def install_library(self, source, data, force, allow_prerelease) -> str: pre_release=allow_prerelease) @RPC.export - def remove_library(self, name): - self._aip.remove_library(name) + def remove_library(self, library: str) -> None: + self._aip.remove_library(library=library) def _raise_error_if_identity_exists_without_force(self, vip_identity: str, force: bool) -> Identity: """ From ddb34ba4463d92d846317ea0bd8fefd8ebc5f11d Mon Sep 17 00:00:00 2001 From: Chandrika Sivaramakrishnan Date: Wed, 1 Apr 2026 15:23:27 -0700 Subject: [PATCH 9/9] added minor stdout --- src/volttron/client/commands/control.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/volttron/client/commands/control.py b/src/volttron/client/commands/control.py index d660a2b45..0bc76e9c3 100644 --- a/src/volttron/client/commands/control.py +++ b/src/volttron/client/commands/control.py @@ -276,6 +276,9 @@ def remove_agent(opts, remove_auth=True): def remove_lib(opts): opts.connection.call("remove_library", opts.library) + sys.stdout.write(f"Removed {opts.library} \n") + + def _calc_min_uuid_length(agents: list[AgentMeta]): n = 0 for agent1 in agents: