Skip to content

Commit 4dcd624

Browse files
committed
Separated the dependencies in extra strings, so the install with no extra is the bare metrics gathering core (without even the docker extra)
1 parent f4f4bf2 commit 4dcd624

8 files changed

Lines changed: 119 additions & 31 deletions

File tree

MANIFEST.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
include LICENSE
22
include README.md
3-
include installation/requirements.txt
3+
include installation/requirements.txt
4+
include installation/requirements-core.txt
5+
include installation/requirements-docker.txt
6+
include installation/requirements-analytics.txt
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pandas
2+
networkx
3+
matplotlib
4+
adjustText

installation/requirements-core.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
psutil
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker

installation/requirements.txt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
psutil
2-
docker
3-
pandas
4-
networkx
5-
matplotlib
6-
adjustText
1+
-r requirements-core.txt
2+
-r requirements-docker.txt
3+
-r requirements-analytics.txt

setup.py

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# SPDX-License-Identifier: GPL-3.0-or-later
55
# treecript, a process tree metrics gatherer.
6-
# Copyright (C) 2025 Barcelona Supercomputing Center, José M. Fernández
6+
# Copyright (C) 2026 Barcelona Supercomputing Center, José M. Fernández
77
#
88
# This program is free software: you can redistribute it and/or modify
99
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,19 @@
2323
import sys
2424
import setuptools
2525

26+
from typing import (
27+
cast,
28+
TYPE_CHECKING,
29+
)
30+
31+
if TYPE_CHECKING:
32+
from typing import (
33+
Final,
34+
MutableMapping,
35+
MutableSequence,
36+
Sequence,
37+
)
38+
2639
# In this way, we are sure we are getting
2740
# the installer's version of the library
2841
# not the system's one
@@ -33,21 +46,65 @@
3346
from treecript import __author__ as epmc_author # noqa: E402
3447
from treecript import __license__ as epmc_license # noqa: E402
3548

49+
EGG_PAT = re.compile(r"#[^#]*egg=([^=&]+)")
50+
OTHER_REQ_PAT = re.compile(r"^-r\s+(.+)")
51+
REQ_SECT_PAT = re.compile(r"^requirements-([^.]+)\.txt$")
52+
53+
CORE_REQUIREMENTS: "Final[str]" = "core"
54+
55+
56+
def populate_extra_requirements(
57+
requirements_path: "str", section: "str" = CORE_REQUIREMENTS
58+
) -> "MutableMapping[str, Sequence[str]]":
59+
extra_requirements: "MutableMapping[str, MutableSequence[str]]" = {}
60+
if os.path.exists(requirements_path):
61+
# Be sure the path is an absolute one
62+
if not os.path.isabs(requirements_path):
63+
requirements_path = os.path.abspath(requirements_path)
64+
65+
with open(requirements_path, mode="r", encoding="utf-8") as f:
66+
for line in f.read().splitlines():
67+
rm = OTHER_REQ_PAT.search(line)
68+
if rm is None:
69+
print(f"R {line}")
70+
m = EGG_PAT.search(line)
71+
extra_requirements.setdefault(section, []).append(
72+
line if m is None else m.group(1)
73+
)
74+
else:
75+
# This is needed for cases like -r
76+
other_requirements_path = os.path.realpath(
77+
os.path.join(os.path.dirname(requirements_path), rm.group(1))
78+
)
79+
other_requirements_basename = os.path.basename(
80+
other_requirements_path
81+
)
82+
83+
nested_section = section
84+
rsmatch = REQ_SECT_PAT.search(other_requirements_basename)
85+
if rsmatch is not None:
86+
nested_section = rsmatch.group(1)
87+
88+
nested_extra_requirements = populate_extra_requirements(
89+
other_requirements_path, section=nested_section
90+
)
91+
for nested_sect, nested_deps in nested_extra_requirements.items():
92+
extra_requirements.setdefault(nested_sect, []).extend(
93+
nested_deps
94+
)
95+
96+
return cast("MutableMapping[str, Sequence[str]]", extra_requirements)
97+
98+
3699
# Populating the long description
37100
readme_path = os.path.join(setupDir, "README.md")
38101
with open(readme_path, "r") as fh:
39102
long_description = fh.read()
40103

41104
# Populating the install requirements
42-
requirements = []
43105
requirements_path = os.path.join(setupDir, "installation", "requirements.txt")
44-
if os.path.exists(requirements_path):
45-
with open(requirements_path, mode="r", encoding="utf-8") as f:
46-
egg = re.compile(r"#[^#]*egg=([^=&]+)")
47-
for line in f.read().splitlines():
48-
print(f"R {line}")
49-
m = egg.search(line)
50-
requirements.append(line if m is None else m.group(1))
106+
extra_requirements = populate_extra_requirements(requirements_path)
107+
requirements = extra_requirements.pop(CORE_REQUIREMENTS, [])
51108

52109
setuptools.setup(
53110
name="treecript",
@@ -78,7 +135,19 @@
78135
"legacy/plotGraph.sh",
79136
"legacy/plot-metrics.sh",
80137
],
138+
entry_points={
139+
"console_scripts": [
140+
"execution-metrics-collector = treecript.collector:main__commandline",
141+
"process-metrics-collector = treecript.collector:main",
142+
"cpuinfo-tdp-finder = treecript.tdp_finder:main_cpuinfo_tdp_finder",
143+
"modelname-tdp-finder = treecript.tdp_finder:main_modelname_tdp_finder",
144+
"tdp-finder = treecript.tdp_finder:main_tdp_finder",
145+
"metrics-aggregator = treecript.aggregator:main [analytics]",
146+
"plotGraph = treecript.plot_graph:main [analytics]",
147+
],
148+
},
81149
install_requires=requirements,
150+
extras_require=extra_requirements,
82151
# See https://pypi.org/classifiers/
83152
classifiers=[
84153
"Programming Language :: Python :: 3",

treecript/__init__.py

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

44
# SPDX-License-Identifier: GPL-3.0-or-later
55
# treecript, a process tree metrics gatherer.
6-
# Copyright (C) 2025 Barcelona Supercomputing Center, José M. Fernández
6+
# Copyright (C) 2026 Barcelona Supercomputing Center, José M. Fernández
77
#
88
# This program is free software: you can redistribute it and/or modify
99
# it under the terms of the GNU General Public License as published by
@@ -23,4 +23,4 @@
2323
__license__ = "GPL-3.0-or-later"
2424

2525
# https://www.python.org/dev/peps/pep-0396/
26-
__version__ = "0.6.1"
26+
__version__ = "0.7.0"

treecript/collector.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import atexit
2222
import copy
2323
import datetime
24-
import docker
2524
import json
2625
import logging
2726
import os
@@ -343,12 +342,23 @@ def process_metrics_collector(
343342
print("\t".join(agg_metrics_cols), file=cH)
344343

345344
docker_cli = None
345+
docker_errors_NotFound = BaseException
346346
if match_docker:
347347
try:
348-
docker_cli = docker.from_env()
349-
except docker.errors.DockerException:
348+
import docker
349+
350+
try:
351+
docker_cli = docker.from_env()
352+
353+
except docker.errors.DockerException:
354+
logger.info(
355+
"Docker service not reachable. Processes spawned using docker will not be properly tracked"
356+
)
357+
finally:
358+
docker_errors_NotFound = docker.errors.NotFound # type: ignore[assignment]
359+
except ModuleNotFoundError:
350360
logger.info(
351-
"Docker service not reachable. Processes spawned using docker will not be properly tracked"
361+
"Docker python feature not installed. Processes spawned using docker will not be properly tracked"
352362
)
353363

354364
recorded_pids: "MutableMapping[int, psutil.Process]" = dict()
@@ -429,16 +439,19 @@ def process_metrics_collector(
429439
container_data.sort(key=lambda c: c[1])
430440

431441
break
432-
except docker.errors.NotFound:
433-
tries -= 1
434-
if tries == 0:
435-
logger.warning(
436-
"Failed getting the list of docker instances after 5 tries"
437-
)
442+
except BaseException as be:
443+
if docker_errors_NotFound != BaseException and isinstance( # noqa: E721
444+
be, docker_errors_NotFound
445+
): # noqa: E721
446+
tries -= 1
447+
if tries == 0:
448+
logger.warning(
449+
"Failed getting the list of docker instances after 5 tries"
450+
)
451+
break
452+
else:
453+
logger.warning("Failed to get the list of docker instances")
438454
break
439-
except BaseException:
440-
logger.warning("Failed to get the list of docker instances")
441-
break
442455

443456
timestamp_str = datetime.datetime.now().strftime(timestamp_format)
444457

0 commit comments

Comments
 (0)