Visitar URL original
Migrate to `pathlib.Path` instead of `str` (first phase) by deruyter92 · Pull Request #3350 · DeepLabCut/DeepLabCut · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3b8c44a
fix path resolution on network drives. Replace Path(..).resolve() wi…
deruyter92 May 29, 2026
fddcbaa
Add safe_resolve helper for path resolution
deruyter92 May 29, 2026
2403b72
replace str with pathlib.Path
deruyter92 May 29, 2026
65f8dfb
fix redundant resolution of resolved path in memory_replay.py
deruyter92 May 29, 2026
6bbe26d
fix open -> os.stat
deruyter92 May 29, 2026
3f93a76
replace os.stat -> open for files, listdir for dirs.
deruyter92 May 29, 2026
87cd6aa
fix TestReadConfigProjectPath
deruyter92 May 29, 2026
8be1c26
Update deeplabcut/utils/auxiliaryfunctions.py
deruyter92 May 29, 2026
f2bb35d
additional replacements: str ->Path
deruyter92 Jun 3, 2026
810406a
Update function signatures str -> str | Path
deruyter92 Jun 4, 2026
c7835bc
Add ruff linting rule PTH
deruyter92 Jun 4, 2026
5419546
Open dataset file directly using Path.open
C-Achard Jun 15, 2026
156c25f
Use context manager when writing pose_cfg.yaml
C-Achard Jun 15, 2026
d854aa5
Pass string path to cv2.VideoCapture
C-Achard Jun 15, 2026
a99f18b
Use explicit loop to unlink frame files
C-Achard Jun 15, 2026
e896398
Fix CircleCI failure
C-Achard Jun 15, 2026
132b882
fix Matplotlib Path import shadowing ->MPLPath
deruyter92 Jun 25, 2026
ee77f84
use pathlib consistently in `metrics`
deruyter92 Jun 25, 2026
0f2d32e
use safe_resolve in export
deruyter92 Jun 25, 2026
421bdc8
remove redundant defensive block for windows paths on python 3.6
deruyter92 Jun 25, 2026
f80c780
remove unused `get_immediate_subdirectories`
deruyter92 Jun 25, 2026
61165bc
use Path in plot_edge_affinity_distributions
deruyter92 Jun 25, 2026
693e945
remove PTH enforcement in linting
deruyter92 Jun 25, 2026
8f3008b
use safe_resolve instead of resolve in `training` and `auxiliaryfunct…
deruyter92 Jun 25, 2026
107a29a
Update GUI assets import (#3370)
C-Achard Jun 25, 2026
5ec1023
Merge remote-tracking branch 'upstream/main' into jaap/safe_resolve_p…
deruyter92 Jun 26, 2026
f422dcc
Merge branch 'main' into jaap/safe_resolve_paths
C-Achard Jun 29, 2026
9d2732b
Adjust config path tests for Path objects
C-Achard Jun 29, 2026
b33ae3e
Merge main in #3350
C-Achard Jun 29, 2026
ad1ae3e
Merge branch 'jaap/safe_resolve_paths' into jaap/pathlib_path_policy
C-Achard Jun 29, 2026
3ab3bd9
Fix os import in safe_resolve
C-Achard Jun 29, 2026
bb23a34
address remaining `.resolve()` cases: prefer `.absolute()`, or otherw…
deruyter92 Jul 6, 2026
831cb1d
update `safe_resolve`: add logging, use os.stat (more efficient)
deruyter92 Jul 6, 2026
77bf7f0
update test safe_resolve: allow fallback and test open/closing instead
deruyter92 Jul 6, 2026
ca63582
Merge remote-tracking branch 'upstream/main' into jaap/safe_resolve_p…
deruyter92 Jul 6, 2026
205b550
fix remaining Path.resolve()
deruyter92 Jul 7, 2026
f1e9d97
Merge remote-tracking branch 'upstream/jaap/safe_resolve_paths'
deruyter92 Jul 7, 2026
c51a25a
fix reintroduced pathlib `resolve()` occurences (see #3349)
deruyter92 Jul 7, 2026
a3b9f12
use pathlib Path in GUI codebase
deruyter92 Jul 7, 2026
09b578c
use pathlib Path in memory replay
deruyter92 Jul 7, 2026
58bc2a4
GUI rename field `config` to `config_path`
deruyter92 Jul 7, 2026
af613f3
GUI use os.fspath for str conversions where required
deruyter92 Jul 7, 2026
1586e2a
use pathlib Path in create_project
deruyter92 Jul 7, 2026
c49d392
use pathlib Path across pose_estimation_pytorch internally
deruyter92 Jul 7, 2026
6a85abb
use centralized yaml dumper (which normalizes Path -> str)
deruyter92 Jul 9, 2026
33c7b58
fix materialize.py downstream old str.split -> Path
deruyter92 Jul 9, 2026
efebf8d
fix testsscripts str usage
deruyter92 Jul 9, 2026
971f381
fix old str usage in trainingsetmanipulation
deruyter92 Jul 9, 2026
292b251
allow str type at TF boundary
deruyter92 Jul 9, 2026
1330eab
allow str formatting in tensorflow code
deruyter92 Jul 9, 2026
84b5c83
fix config update in train_from_coco.py
deruyter92 Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
replace str with pathlib.Path
  • Loading branch information
deruyter92 committed May 29, 2026
commit 2403b72329277bf40d273bc975e1a64debcdbfef
4 changes: 2 additions & 2 deletions deeplabcut/create_project/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def create_new_project(
# Create project and sub-directories
if not DEBUG and project_path.exists():
print(f'Project "{project_path}" already exists!')
return os.path.join(str(project_path), "config.yaml")
return str(project_path / "config.yaml")
video_path = project_path / "videos"
data_path = project_path / "labeled-data"
shuffles_path = project_path / "training-datasets"
Expand Down Expand Up @@ -304,7 +304,7 @@ def create_new_project(
cfg_file["alphavalue"] = 0.7 # for plots transparency of markers
cfg_file["colormap"] = "rainbow" # for plots type of colormap

projconfigfile = os.path.join(str(project_path), "config.yaml")
projconfigfile = project_path / "config.yaml"
# Write dictionary to yaml config file
auxiliaryfunctions.write_config(projconfigfile, cfg_file)

Expand Down
3 changes: 1 addition & 2 deletions deeplabcut/create_project/new_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#


import os
from pathlib import Path

from deeplabcut import DEBUG
Expand Down Expand Up @@ -122,7 +121,7 @@ def create_new_project_3d(project, experimenter, num_cameras=2, working_director
cfg_file_3d.insert(len(cfg_file_3d), str("shuffle_camera-" + str(i + 1)), 1)
cfg_file_3d.insert(len(cfg_file_3d), str("trainingsetindex_camera-" + str(i + 1)), 0)

projconfigfile = os.path.join(str(project_path), "config.yaml")
projconfigfile = project_path / "config.yaml"
auxiliaryfunctions.write_config_3d(projconfigfile, cfg_file_3d)

print('Generated "{}"'.format(project_path / "config.yaml"))
Expand Down
20 changes: 10 additions & 10 deletions deeplabcut/generate_training_dataset/trainingsetmanipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ def dropduplicatesinannotatinfiles(config):

for folder in folders:
try:
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5")
fn = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
DC = pd.read_hdf(fn)
numimages = len(DC.index)
DC = DC[~DC.index.duplicated(keep="first")]
if len(DC.index) < numimages:
print("Dropped", numimages - len(DC.index))
DC.to_hdf(fn, key="df_with_missing", mode="w")
DC.to_csv(os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".csv"))
DC.to_csv(folder / ("CollectedData_" + cfg["scorer"] + ".csv"))

except FileNotFoundError:
print("Attention:", folder, "does not appear to have labeled data!")
Expand All @@ -168,7 +168,7 @@ def dropannotationfileentriesduetodeletedimages(config):
folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]

for folder in folders:
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5")
fn = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
try:
DC = pd.read_hdf(fn)
except FileNotFoundError:
Expand All @@ -184,7 +184,7 @@ def dropannotationfileentriesduetodeletedimages(config):
dropped = True
if dropped:
DC.to_hdf(fn, key="df_with_missing", mode="w")
DC.to_csv(os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".csv"))
DC.to_csv(folder / ("CollectedData_" + cfg["scorer"] + ".csv"))


def dropimagesduetolackofannotation(config):
Expand All @@ -203,15 +203,15 @@ def dropimagesduetolackofannotation(config):
folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]

for folder in folders:
h5file = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5")
h5file = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
try:
DC = pd.read_hdf(h5file)
except FileNotFoundError:
print("Attention:", folder, "does not appear to have labeled data!")
continue
conversioncode.guarantee_multiindex_rows(DC)
annotatedimages = [fn[-1] for fn in DC.index]
imagelist = [fns for fns in os.listdir(str(folder)) if ".png" in fns]
imagelist = [fns for fns in os.listdir(folder) if ".png" in fns]
print("Annotated images: ", len(annotatedimages), " In folder:", len(imagelist))
for imagename in imagelist:
if imagename in annotatedimages:
Expand Down Expand Up @@ -250,7 +250,7 @@ def dropunlabeledframes(config):
folders = [Path(config).parent / "labeled-data" / Path(i) for i in video_names]

for folder in folders:
h5file = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5")
h5file = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
try:
DC = pd.read_hdf(h5file)
except FileNotFoundError:
Expand All @@ -262,7 +262,7 @@ def dropunlabeledframes(config):
dropped = before_len - after_len
if dropped:
DC.to_hdf(h5file, key="df_with_missing", mode="w")
DC.to_csv(os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".csv"))
DC.to_csv(folder / ("CollectedData_" + cfg["scorer"] + ".csv"))

print("Dropped ", dropped, "entries in ", folder)

Expand Down Expand Up @@ -327,11 +327,11 @@ def check_labels(
videos = cfg["video_sets"].keys()
video_names = [_robust_path_split(video)[1] for video in videos]

folders = [os.path.join(cfg["project_path"], "labeled-data", str(Path(i))) for i in video_names]
folders = [Path(cfg["project_path"]) / "labeled-data" / Path(i) for i in video_names]
print("Creating images with labels by {}.".format(cfg["scorer"]))
for folder in folders:
try:
DataCombined = pd.read_hdf(os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5"))
DataCombined = pd.read_hdf(folder / ("CollectedData_" + cfg["scorer"] + ".h5"))
conversioncode.guarantee_multiindex_rows(DataCombined)
if cfg.get("multianimalproject", False):
color_by = "individual" if visualizeindividuals else "bodypart"
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/gui/displays/selected_shuffle_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _set_text_error(self, error: str) -> None:
self.pose_cfg = None

def _read_pose_config(self, pose_cfg_path: Path) -> None:
pose_cfg = auxiliaryfunctions.read_plainconfig(str(pose_cfg_path))
pose_cfg = auxiliaryfunctions.read_plainconfig(pose_cfg_path)

self._engine = Engine.PYTORCH if "pytorch" in pose_cfg_path.stem.lower() else Engine.TF
self._net_type = pose_cfg.get("net_type", "UNKNOWN")
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/pose_estimation_pytorch/apis/tracklets.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def convert_detections2tracklets(
)
model_dir = Path(cfg["project_path"]) / rel_model_dir
path_test_config = model_dir / "test" / "pose_cfg.yaml"
dlc_cfg = auxiliaryfunctions.read_plainconfig(str(path_test_config))
dlc_cfg = auxiliaryfunctions.read_plainconfig(path_test_config)

if "multi-animal" not in dlc_cfg["dataset_type"]:
raise ValueError("This function is only required for multianimal projects!")
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/pose_estimation_tensorflow/core/evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ def evaluate_network(
cfg, comparisonbodyparts
)
# Make folder for evaluation
auxiliaryfunctions.attempt_to_make_folder(str(cfg["project_path"] + "/evaluation-results/"))
auxiliaryfunctions.attempt_to_make_folder(Path(cfg["project_path"]) / "evaluation-results")
for shuffle in Shuffles:
for trainFraction in TrainingFractions:
##################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def evaluate_multianimal_full(
all_bpts = np.asarray(len(cfg["individuals"]) * cfg["multianimalbodyparts"] + cfg["uniquebodyparts"])
colors = visualization.get_cmap(len(comparisonbodyparts), name=cfg["colormap"])
# Make folder for evaluation
auxiliaryfunctions.attempt_to_make_folder(str(cfg["project_path"] + "/evaluation-results/"))
auxiliaryfunctions.attempt_to_make_folder(Path(cfg["project_path"]) / "evaluation-results")
for shuffle in Shuffles:
for trainFraction in TrainingFractions:
##################################################
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/pose_estimation_tensorflow/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ def export_model(

### set up export directory

export_dir = os.path.normpath(cfg["project_path"] + "/" + "exported-models")
export_dir = Path(cfg["project_path"]) / "exported-models"
if not os.path.isdir(export_dir):
os.mkdir(export_dir)

sub_dir_name = f"DLC_{cfg['Task']}_{dlc_cfg['net_type']}_iteration-{cfg['iteration']}_shuffle-{shuffle}"
full_export_dir = os.path.normpath(export_dir + "/" + sub_dir_name)
full_export_dir = export_dir / sub_dir_name

if os.path.isdir(full_export_dir):
if not overwrite:
Expand Down
4 changes: 2 additions & 2 deletions deeplabcut/pose_estimation_tensorflow/visualizemaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Licensed under GNU Lesser General Public License v3.0
#
import os
from pathlib import Path

import matplotlib.pyplot as plt
from skimage.transform import resize
Expand Down Expand Up @@ -61,7 +62,6 @@ def extract_maps(
If you want to extract the data for image 0 and 103 (of the training set) for model trained with shuffle 0.
>>> deeplabcut.extract_maps(configfile,0,Indices=[0,103])
"""
from pathlib import Path

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -114,7 +114,7 @@ def extract_maps(
)

# Make folder for evaluation
auxiliaryfunctions.attempt_to_make_folder(str(cfg["project_path"] + "/evaluation-results/"))
auxiliaryfunctions.attempt_to_make_folder(Path(cfg["project_path"]) / "evaluation-results")

Maps = {}
for trainFraction in TrainingFractions:
Expand Down
12 changes: 6 additions & 6 deletions deeplabcut/utils/auxfun_multianimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def returnlabelingdata(config):
print("Do you want to get the data for folder:", folder, "?")
askuser = input("yes/no")
if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha": # multilanguage support :)
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".h5")
fn = folder / ("CollectedData_" + cfg["scorer"] + ".h5")
Data = pd.read_hdf(fn)
return Data

Expand Down Expand Up @@ -292,8 +292,8 @@ def convert2_maDLC(config, userfeedback=True, forceindividual=None):
askuser = "yes"

if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha": # multilanguage support :)
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"])
Data = pd.read_hdf(fn + ".h5")
fn = folder / ("CollectedData_" + cfg["scorer"])
Data = pd.read_hdf(str(fn) + ".h5")
conversioncode.guarantee_multiindex_rows(Data)
imindex = Data.index

Expand Down Expand Up @@ -380,8 +380,8 @@ def convert_single2multiplelegacyAM(config, userfeedback=True, target=None):
askuser = "yes"

if askuser == "y" or askuser == "yes" or askuser == "Ja" or askuser == "ha": # multilanguage support :)
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"])
Data = pd.read_hdf(fn + ".h5")
fn = folder / ("CollectedData_" + cfg["scorer"])
Data = pd.read_hdf(str(fn) + ".h5")
conversioncode.guarantee_multiindex_rows(Data)
imindex = Data.index

Expand Down Expand Up @@ -544,7 +544,7 @@ def check_inferencecfg_sanity(cfg, inferencecfg):
def read_inferencecfg(path_inference_config, cfg):
"""Load inferencecfg or initialize it."""
try:
inferencecfg = auxiliaryfunctions.read_plainconfig(str(path_inference_config))
inferencecfg = auxiliaryfunctions.read_plainconfig(path_inference_config)
except FileNotFoundError:
inferencecfg = form_default_inferencecfg(cfg)
auxiliaryfunctions.write_plainconfig(str(path_inference_config), dict(inferencecfg))
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/utils/conversioncode.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def convertcsv2h5(config, userfeedback=True, scorer=None):
askuser = "yes"

if askuser in ("y", "yes", "Ja", "ha", "oui"): # multilanguage support :)
fn = os.path.join(str(folder), "CollectedData_" + cfg["scorer"] + ".csv")
fn = folder / ("CollectedData_" + cfg["scorer"] + ".csv")
# Determine whether the data are single- or multi-animal without loading into memory
# simply by checking whether 'individuals' is in the second line of the CSV.
with open(fn) as datafile:
Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/utils/make_labeled_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ def create_labeled_video(
)
model_config_path = Path(config).parent / model_folder / "train" / Engine.PYTORCH.pose_cfg_name
if model_config_path.exists():
model_config = auxiliaryfunctions.read_plainconfig(str(model_config_path))
model_config = auxiliaryfunctions.read_plainconfig(model_config_path)
if model_config["train_settings"].get("weight_init", {}).get("memory_replay", False):
superanimal_name = model_config["train_settings"]["weight_init"]["dataset"]
if bboxes_pcutoff is None:
Expand Down
Loading