Skip to content

Commit 9d1f066

Browse files
NotWearingPantspre-commit-ci[bot]behackl
authored
Migrate more os.path to pathlib (#2980)
* Migrate more `os.path` to `pathlib` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix type errors with recent pathlib code * pathlib fixes * more pathlib fixes * remove unused imports introduced by pathlib migration * convert `open()` calls to pathlib * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Migrate tex_file_writing to pathlib * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * converted more old code to pathlib, and fixed a bug in module_ops * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test failures * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test failures * Apply suggestions from code review Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
1 parent 3c172e2 commit 9d1f066

21 files changed

+212
-272
lines changed

manim/_config/utils.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ def config_file_paths() -> list[Path]:
7474
return [library_wide, user_wide, folder_wide]
7575

7676

77-
def make_config_parser(custom_file: str = None) -> configparser.ConfigParser:
77+
def make_config_parser(
78+
custom_file: str | os.PathLike | None = None,
79+
) -> configparser.ConfigParser:
7880
"""Make a :class:`ConfigParser` object and load any ``.cfg`` files.
7981
8082
The user-wide file, if it exists, overrides the library-wide file. The
@@ -85,7 +87,7 @@ def make_config_parser(custom_file: str = None) -> configparser.ConfigParser:
8587
8688
Parameters
8789
----------
88-
custom_file : :class:`str`
90+
custom_file
8991
Path to a custom config file. If used, the folder-wide file in the
9092
relevant directory will be ignored, if it exists. If None, the
9193
folder-wide file will be used, if it exists.
@@ -108,10 +110,10 @@ def make_config_parser(custom_file: str = None) -> configparser.ConfigParser:
108110
# read_file() before calling read() for any optional files."
109111
# https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.read
110112
parser = configparser.ConfigParser()
111-
with open(library_wide) as file:
113+
with library_wide.open() as file:
112114
parser.read_file(file) # necessary file
113115

114-
other_files = [user_wide, custom_file if custom_file else folder_wide]
116+
other_files = [user_wide, Path(custom_file) if custom_file else folder_wide]
115117
parser.read(other_files) # optional files
116118

117119
return parser
@@ -809,7 +811,7 @@ def digest_args(self, args: argparse.Namespace) -> ManimConfig:
809811

810812
return self
811813

812-
def digest_file(self, filename: str) -> ManimConfig:
814+
def digest_file(self, filename: str | os.PathLike) -> ManimConfig:
813815
"""Process the config options present in a ``.cfg`` file.
814816
815817
This method processes a single ``.cfg`` file, whereas
@@ -818,7 +820,7 @@ def digest_file(self, filename: str) -> ManimConfig:
818820
819821
Parameters
820822
----------
821-
filename : :class:`str`
823+
filename
822824
Path to the ``.cfg`` file.
823825
824826
Returns
@@ -840,11 +842,11 @@ def digest_file(self, filename: str) -> ManimConfig:
840842
multiple times.
841843
842844
"""
843-
if not os.path.isfile(filename):
845+
if not Path(filename).is_file():
844846
raise FileNotFoundError(
845847
errno.ENOENT,
846848
"Error: --config_file could not find a valid config file.",
847-
filename,
849+
str(filename),
848850
)
849851

850852
return self.digest_parser(make_config_parser(filename))

manim/cli/cfg/group.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def write(level: str = None, openfile: bool = False) -> None:
233233
cfg_file_path = config_paths[2]
234234
guarantee_existence(config_paths[2].parents[0])
235235
console.print(CWD_CONFIG_MSG)
236-
with open(cfg_file_path, "w") as fp:
236+
with cfg_file_path.open("w") as fp:
237237
parser.write(fp)
238238
if openfile:
239239
open_file(cfg_file_path)

manim/cli/init/commands.py

+10-14
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def update_cfg(cfg_dict, project_cfg_path):
7474
else:
7575
cli_config[key] = str(value)
7676

77-
with open(project_cfg_path, "w") as conf:
77+
with project_cfg_path.open("w") as conf:
7878
config.write(conf)
7979

8080

@@ -154,32 +154,28 @@ def scene(**args):
154154
type=click.Choice(get_template_names(), False),
155155
default="Default",
156156
)
157-
scene = ""
158-
with open(Path.resolve(get_template_path() / f"{template_name}.mtp")) as f:
159-
scene = f.read()
160-
scene = scene.replace(template_name + "Template", args["scene_name"], 1)
157+
scene = (get_template_path() / f"{template_name}.mtp").resolve().read_text()
158+
scene = scene.replace(template_name + "Template", args["scene_name"], 1)
161159

162160
if args["file_name"]:
163-
if args["file_name"][-3:] == ".py":
164-
file_name = args["file_name"]
165-
else:
166-
file_name = args["file_name"] + ".py"
161+
file_name = Path(args["file_name"])
162+
163+
if file_name.suffix != ".py":
164+
file_name = file_name.with_suffix(file_name.suffix + ".py")
167165

168-
file_name = Path(file_name)
169166
if file_name.is_file():
170167
# file exists so we are going to append new scene to that file
171-
with open(file_name, "a") as f:
168+
with file_name.open("a") as f:
172169
f.write("\n\n\n" + scene)
173170
else:
174171
# file does not exist so we create a new file, append the scene and prepend the import statement
175-
with open(file_name, "w") as f:
176-
f.write("\n\n\n" + scene)
172+
file_name.write_text("\n\n\n" + scene)
177173

178174
add_import_statement(file_name)
179175
else:
180176
# file name is not provided so we assume it is main.py
181177
# if main.py does not exist we do not continue
182-
with open(Path("main.py"), "a") as f:
178+
with Path("main.py").open("a") as f:
183179
f.write("\n\n\n" + scene)
184180

185181

manim/cli/new/group.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def update_cfg(cfg_dict, project_cfg_path):
6767
else:
6868
cli_config[key] = str(value)
6969

70-
with open(project_cfg_path, "w") as conf:
70+
with project_cfg_path.open("w") as conf:
7171
config.write(conf)
7272

7373

@@ -150,28 +150,25 @@ def scene(**args):
150150
type=click.Choice(get_template_names(), False),
151151
default="Default",
152152
)
153-
scene = ""
154-
with open(Path.resolve(get_template_path() / f"{template_name}.mtp")) as f:
155-
scene = f.read()
156-
scene = scene.replace(template_name + "Template", args["scene_name"], 1)
153+
scene = (get_template_path() / f"{template_name}.mtp").resolve().read_text()
154+
scene = scene.replace(template_name + "Template", args["scene_name"], 1)
157155

158156
if args["file_name"]:
159157
file_name = Path(args["file_name"] + ".py")
160158

161159
if file_name.is_file():
162160
# file exists so we are going to append new scene to that file
163-
with open(file_name, "a") as f:
161+
with file_name.open("a") as f:
164162
f.write("\n\n\n" + scene)
165163
else:
166164
# file does not exist so we create a new file, append the scene and prepend the import statement
167-
with open(file_name, "w") as f:
168-
f.write("\n\n\n" + scene)
165+
file_name.write_text("\n\n\n" + scene)
169166

170167
add_import_statement(file_name)
171168
else:
172169
# file name is not provided so we assume it is main.py
173170
# if main.py does not exist we do not continue
174-
with open(Path("main.py"), "a") as f:
171+
with Path("main.py").open("a") as f:
175172
f.write("\n\n\n" + scene)
176173

177174

manim/mobject/svg/svg_mobject.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import os
6+
from pathlib import Path
67
from xml.etree import ElementTree as ET
78

89
import numpy as np
@@ -86,7 +87,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
8687

8788
def __init__(
8889
self,
89-
file_name: str | None = None,
90+
file_name: str | os.PathLike | None = None,
9091
should_center: bool = True,
9192
height: float | None = 2,
9293
width: float | None = None,
@@ -104,7 +105,7 @@ def __init__(
104105
super().__init__(color=None, stroke_color=None, fill_color=None, **kwargs)
105106

106107
# process keyword arguments
107-
self.file_name = file_name
108+
self.file_name = Path(file_name) if file_name is not None else None
108109

109110
self.should_center = should_center
110111
self.svg_height = height
@@ -182,18 +183,17 @@ def generate_mobject(self) -> None:
182183
element_tree = ET.parse(file_path)
183184
new_tree = self.modify_xml_tree(element_tree)
184185
# Create a temporary svg file to dump modified svg to be parsed
185-
root, ext = os.path.splitext(file_path)
186-
modified_file_path = root + "_" + ext
186+
modified_file_path = file_path.with_name(f"{file_path.stem}_{file_path.suffix}")
187187
new_tree.write(modified_file_path)
188188

189189
svg = se.SVG.parse(modified_file_path)
190-
os.remove(modified_file_path)
190+
modified_file_path.unlink()
191191

192192
mobjects = self.get_mobjects_from(svg)
193193
self.add(*mobjects)
194194
self.flip(RIGHT) # Flip y
195195

196-
def get_file_path(self) -> str:
196+
def get_file_path(self) -> Path:
197197
"""Search for an existing file based on the specified file name."""
198198
if self.file_name is None:
199199
raise ValueError("Must specify file for SVGMobject")

manim/mobject/text/code_mobject.py

+13-25
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
]
88

99
import html
10-
import os
1110
import re
11+
from pathlib import Path
1212

1313
import numpy as np
1414
from pygments import highlight
@@ -88,7 +88,7 @@ def construct(self):
8888
8989
Parameters
9090
----------
91-
file_name : :class:`str`
91+
file_name
9292
Name of the code file to display.
9393
code : :class:`str`
9494
If ``file_name`` is not specified, a code string can be
@@ -153,7 +153,7 @@ def construct(self):
153153

154154
def __init__(
155155
self,
156-
file_name=None,
156+
file_name: str | os.PathLike = None,
157157
code=None,
158158
tab_width=3,
159159
line_spacing=0.3,
@@ -199,8 +199,7 @@ def __init__(
199199
self.file_name = file_name
200200
if self.file_name:
201201
self._ensure_valid_file()
202-
with open(self.file_path, encoding="utf-8") as f:
203-
self.code_string = f.read()
202+
self.code_string = self.file_path.read_text(encoding="utf-8")
204203
elif code:
205204
self.code_string = code
206205
else:
@@ -284,16 +283,16 @@ def _ensure_valid_file(self):
284283
if self.file_name is None:
285284
raise Exception("Must specify file for Code")
286285
possible_paths = [
287-
os.path.join(os.path.join("assets", "codes"), self.file_name),
288-
os.path.expanduser(self.file_name),
286+
Path() / "assets" / "codes" / self.file_name,
287+
Path(self.file_name).expanduser(),
289288
]
290289
for path in possible_paths:
291-
if os.path.exists(path):
290+
if path.exists():
292291
self.file_path = path
293292
return
294293
error = (
295-
f"From: {os.getcwd()}, could not find {self.file_name} at either "
296-
+ f"of these locations: {possible_paths}"
294+
f"From: {Path.cwd()}, could not find {self.file_name} at either "
295+
+ f"of these locations: {list(map(str, possible_paths))}"
297296
)
298297
raise OSError(error)
299298

@@ -369,20 +368,9 @@ def _gen_html_string(self):
369368
)
370369

371370
if self.generate_html_file:
372-
os.makedirs(
373-
os.path.join("assets", "codes", "generated_html_files"),
374-
exist_ok=True,
375-
)
376-
with open(
377-
os.path.join(
378-
"assets",
379-
"codes",
380-
"generated_html_files",
381-
self.file_name + ".html",
382-
),
383-
"w",
384-
) as file:
385-
file.write(self.html_string)
371+
output_folder = Path() / "assets" / "codes" / "generated_html_files"
372+
output_folder.mkdir(parents=True, exist_ok=True)
373+
(output_folder / f"{self.file_name}.html").write_text(self.html_string)
386374

387375
def _gen_code_json(self):
388376
"""Function to background_color, generate code_json and tab_spaces from html_string.
@@ -559,7 +547,7 @@ def _hilite_me(
559547
Defines whether line numbers should be inserted in the html file.
560548
divstyles : :class:`str`
561549
Some html css styles.
562-
file_path : :class:`str`
550+
file_path : :class:`pathlib.Path`
563551
Path of code file.
564552
line_no_from : :class:`int`
565553
Defines the first line's number in the line count.

manim/renderer/shader.py

+14-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import os
43
import re
54
import textwrap
65
from pathlib import Path
@@ -24,24 +23,23 @@
2423
]
2524

2625

27-
def get_shader_code_from_file(file_path):
26+
def get_shader_code_from_file(file_path: Path) -> str:
2827
if file_path in file_path_to_code_map:
2928
return file_path_to_code_map[file_path]
30-
with open(file_path) as f:
31-
source = f.read()
32-
include_lines = re.finditer(
33-
r"^#include (?P<include_path>.*\.glsl)$",
34-
source,
35-
flags=re.MULTILINE,
29+
source = file_path.read_text()
30+
include_lines = re.finditer(
31+
r"^#include (?P<include_path>.*\.glsl)$",
32+
source,
33+
flags=re.MULTILINE,
34+
)
35+
for match in include_lines:
36+
include_path = match.group("include_path")
37+
included_code = get_shader_code_from_file(
38+
file_path.parent / include_path,
3639
)
37-
for match in include_lines:
38-
include_path = match.group("include_path")
39-
included_code = get_shader_code_from_file(
40-
os.path.join(file_path.parent / include_path),
41-
)
42-
source = source.replace(match.group(0), included_code)
43-
file_path_to_code_map[file_path] = source
44-
return source
40+
source = source.replace(match.group(0), included_code)
41+
file_path_to_code_map[file_path] = source
42+
return source
4543

4644

4745
def filter_attributes(unfiltered_attributes, attributes):

0 commit comments

Comments
 (0)