Skip to content

Commit 6999c5d

Browse files
committed
feat: switch code formatting to ruff
- Switch to `argparse` and clean up imports in `commands.py`. - Remove `requires-optional.txt` and `test_requirements/*` (put dependencies in `pyproject.toml` instead). - Remove `black` and use `ruff` instead. - Add `--noformat` option to `python commands.py codegen` for experimenting. - Pass output directory around to control what is formatted and linted. - Add `python commands.py format` to only do formatting. - Add `python commands.py lint` to check code. - Reformat comments in `codegen/*.py`. - Use double-quoted strings for (most) code generation. - Incorporate pandas version pinning from #5219. Note: 1. Only the generated code is formatted and linted: - `plotly/validators/**/*.py` - `plotly/graph_objs/**/*.py` - `plotly/graph_objects/__init__.py` 2. The strings in the data used by code generation are (for example) `"'some_name'"` (i.e., have embedded single quotes). This PR does not try to fix that: instead, we rely on reformatting to turn all the single-quoted strings into double-quoted strings.
1 parent a76b656 commit 6999c5d

File tree

11 files changed

+291
-376
lines changed

11 files changed

+291
-376
lines changed

codegen/__init__.py

Lines changed: 59 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os.path as opath
44
import shutil
55
import subprocess
6+
import sys
67

78
from codegen.datatypes import build_datatype_py, write_datatype_py
89
from codegen.compatibility import (
@@ -26,10 +27,6 @@
2627
get_data_validator_instance,
2728
)
2829

29-
# Target Python version for code formatting with Black.
30-
# Must be one of the values listed in pyproject.toml.
31-
BLACK_TARGET_VERSIONS = "py38 py39 py310 py311 py312"
32-
3330

3431
# Import notes
3532
# ------------
@@ -39,15 +36,14 @@
3936
# helpers that are only needed during code generation should reside in the
4037
# codegen/ package, and helpers used both during code generation and at
4138
# runtime should reside in the _plotly_utils/ package.
42-
# ----------------------------------------------------------------------------
39+
4340
def preprocess_schema(plotly_schema):
4441
"""
4542
Central location to make changes to schema before it's seen by the
4643
PlotlyNode classes
4744
"""
4845

4946
# Update template
50-
# ---------------
5147
layout = plotly_schema["layout"]["layoutAttributes"]
5248

5349
# Create codegen-friendly template scheme
@@ -89,33 +85,41 @@ def preprocess_schema(plotly_schema):
8985
items["colorscale"] = items.pop("concentrationscales")
9086

9187

92-
def perform_codegen(reformat=True):
93-
# Set root codegen output directory
94-
# ---------------------------------
95-
# (relative to project root)
96-
abs_file_path = opath.realpath(__file__)
97-
project_root = opath.dirname(opath.dirname(abs_file_path))
98-
outdir = opath.join(project_root, "plotly")
88+
def make_paths(outdir):
89+
"""Make various paths needed for formatting and linting."""
90+
91+
validators_dir = opath.join(outdir, "validators")
92+
graph_objs_dir = opath.join(outdir, "graph_objs")
93+
graph_objects_path = opath.join(outdir, "graph_objects", "__init__.py")
94+
return validators_dir, graph_objs_dir, graph_objects_path
95+
96+
97+
def lint_code(outdir):
98+
"""Check Python code using settings in pyproject.toml."""
99+
100+
subprocess.call(["ruff", "check", *make_paths(outdir)])
101+
102+
103+
def reformat_code(outdir):
104+
"""Reformat Python code using settings in pyproject.toml."""
105+
106+
subprocess.call(["ruff", "format", *make_paths(outdir)])
107+
108+
109+
def perform_codegen(outdir, noformat=False):
110+
"""Generate code (and possibly reformat)."""
111+
112+
# Get paths
113+
validators_dir, graph_objs_dir, graph_objects_path = make_paths(outdir)
99114

100115
# Delete prior codegen output
101-
# ---------------------------
102-
validators_pkgdir = opath.join(outdir, "validators")
103-
if opath.exists(validators_pkgdir):
104-
shutil.rmtree(validators_pkgdir)
105-
106-
graph_objs_pkgdir = opath.join(outdir, "graph_objs")
107-
if opath.exists(graph_objs_pkgdir):
108-
shutil.rmtree(graph_objs_pkgdir)
109-
110-
# plotly/datatypes is not used anymore, but was at one point so we'll
111-
# still delete it if we find it in case a developer is upgrading from an
112-
# older version
113-
datatypes_pkgdir = opath.join(outdir, "datatypes")
114-
if opath.exists(datatypes_pkgdir):
115-
shutil.rmtree(datatypes_pkgdir)
116+
if opath.exists(validators_dir):
117+
shutil.rmtree(validators_dir)
118+
if opath.exists(graph_objs_dir):
119+
shutil.rmtree(graph_objs_dir)
116120

117121
# Load plotly schema
118-
# ------------------
122+
project_root = opath.dirname(outdir)
119123
plot_schema_path = opath.join(
120124
project_root, "codegen", "resources", "plot-schema.json"
121125
)
@@ -124,19 +128,17 @@ def perform_codegen(reformat=True):
124128
plotly_schema = json.load(f)
125129

126130
# Preprocess Schema
127-
# -----------------
128131
preprocess_schema(plotly_schema)
129132

130133
# Build node lists
131-
# ----------------
132-
# ### TraceNode ###
134+
# TraceNode
133135
base_traces_node = TraceNode(plotly_schema)
134136
compound_trace_nodes = PlotlyNode.get_all_compound_datatype_nodes(
135137
plotly_schema, TraceNode
136138
)
137139
all_trace_nodes = PlotlyNode.get_all_datatype_nodes(plotly_schema, TraceNode)
138140

139-
# ### LayoutNode ###
141+
# LayoutNode
140142
compound_layout_nodes = PlotlyNode.get_all_compound_datatype_nodes(
141143
plotly_schema, LayoutNode
142144
)
@@ -155,14 +157,14 @@ def perform_codegen(reformat=True):
155157
if node.is_array_element and node.has_child("xref") and node.has_child("yref")
156158
]
157159

158-
# ### FrameNode ###
160+
# FrameNode
159161
compound_frame_nodes = PlotlyNode.get_all_compound_datatype_nodes(
160162
plotly_schema, FrameNode
161163
)
162164
frame_node = compound_frame_nodes[0]
163165
all_frame_nodes = PlotlyNode.get_all_datatype_nodes(plotly_schema, FrameNode)
164166

165-
# ### All nodes ###
167+
# All nodes
166168
all_datatype_nodes = all_trace_nodes + all_layout_nodes + all_frame_nodes
167169

168170
all_compound_nodes = [
@@ -172,37 +174,34 @@ def perform_codegen(reformat=True):
172174
]
173175

174176
# Write out validators
175-
# --------------------
176-
# # ### Layout ###
177+
178+
# # Layout
177179
for node in all_layout_nodes:
178180
write_validator_py(outdir, node)
179181

180-
# ### Trace ###
182+
# Trace
181183
for node in all_trace_nodes:
182184
write_validator_py(outdir, node)
183185

184-
# ### Frames ###
186+
# Frames
185187
for node in all_frame_nodes:
186188
write_validator_py(outdir, node)
187189

188-
# ### Data (traces) validator ###
190+
# Data (traces) validator
189191
write_data_validator_py(outdir, base_traces_node)
190192

191193
# Alls
192-
# ----
193194
alls = {}
194195

195196
# Write out datatypes
196-
# -------------------
197197
for node in all_compound_nodes:
198198
write_datatype_py(outdir, node)
199199

200-
# ### Deprecated ###
200+
# Deprecated
201201
# These are deprecated legacy datatypes like graph_objs.Marker
202202
write_deprecated_datatypes(outdir)
203203

204204
# Write figure class to graph_objs
205-
# --------------------------------
206205
data_validator = get_data_validator_instance(base_traces_node)
207206
layout_validator = layout_node.get_validator_instance()
208207
frame_validator = frame_node.get_validator_instance()
@@ -218,8 +217,7 @@ def perform_codegen(reformat=True):
218217
)
219218

220219
# Write validator __init__.py files
221-
# ---------------------------------
222-
# ### Write __init__.py files for each validator package ###
220+
# Write __init__.py files for each validator package
223221
validator_rel_class_imports = {}
224222
for node in all_datatype_nodes:
225223
if node.is_mapped:
@@ -239,7 +237,6 @@ def perform_codegen(reformat=True):
239237
write_init_py(validators_pkg, path_parts, [], rel_classes)
240238

241239
# Write datatype __init__.py files
242-
# --------------------------------
243240
datatype_rel_class_imports = {}
244241
datatype_rel_module_imports = {}
245242

@@ -257,16 +254,16 @@ def perform_codegen(reformat=True):
257254
f".{node.name_undercase}"
258255
)
259256

260-
# ### Write plotly/graph_objs/graph_objs.py ###
261-
# This if for backward compatibility. It just imports everything from
257+
# Write plotly/graph_objs/graph_objs.py
258+
# This is for backward compatibility. It just imports everything from
262259
# graph_objs/__init__.py
263260
write_graph_objs_graph_objs(outdir)
264261

265-
# ### Add Figure and FigureWidget ###
262+
# Add Figure and FigureWidget
266263
root_datatype_imports = datatype_rel_class_imports[()]
267264
root_datatype_imports.append("._figure.Figure")
268265

269-
# ### Add deprecations ###
266+
# Add deprecations
270267
for dep_clas in DEPRECATED_DATATYPES:
271268
root_datatype_imports.append(f"._deprecations.{dep_clas}")
272269

@@ -302,14 +299,14 @@ def __getattr__(import_name):
302299
303300
return orig_getattr(import_name)
304301
"""
305-
# ### __all__ ###
302+
# __all__
306303
for path_parts, class_names in alls.items():
307304
if path_parts and class_names:
308305
filepath = opath.join(outdir, "graph_objs", *path_parts, "__init__.py")
309306
with open(filepath, "at") as f:
310307
f.write(f"\n__all__ = {class_names}")
311308

312-
# ### Output datatype __init__.py files ###
309+
# Output datatype __init__.py files
313310
graph_objs_pkg = opath.join(outdir, "graph_objs")
314311
for path_parts in datatype_rel_class_imports:
315312
rel_classes = sorted(datatype_rel_class_imports[path_parts])
@@ -320,7 +317,7 @@ def __getattr__(import_name):
320317
init_extra = ""
321318
write_init_py(graph_objs_pkg, path_parts, rel_modules, rel_classes, init_extra)
322319

323-
# ### Output graph_objects.py alias
320+
# Output graph_objects.py alias
324321
graph_objects_rel_classes = [
325322
"..graph_objs." + rel_path.split(".")[-1]
326323
for rel_path in datatype_rel_class_imports[()]
@@ -335,22 +332,19 @@ def __getattr__(import_name):
335332
graph_objects_rel_classes,
336333
init_extra=optional_figure_widget_import,
337334
)
338-
graph_objects_path = opath.join(outdir, "graph_objects", "__init__.py")
339335
os.makedirs(opath.join(outdir, "graph_objects"), exist_ok=True)
340336
with open(graph_objects_path, "wt") as f:
341337
f.write(graph_objects_init_source)
342338

343-
# ### Run black code formatter on output directories ###
344-
if reformat:
345-
target_version = [
346-
f"--target-version={v}" for v in BLACK_TARGET_VERSIONS.split()
347-
]
348-
subprocess.call(["black", *target_version, validators_pkgdir])
349-
subprocess.call(["black", *target_version, graph_objs_pkgdir])
350-
subprocess.call(["black", *target_version, graph_objects_path])
351-
else:
339+
# Run black code formatter on output directories
340+
if noformat:
352341
print("skipping reformatting")
342+
else:
343+
reformat_code(outdir)
353344

354345

355346
if __name__ == "__main__":
356-
perform_codegen()
347+
if len(sys.argv) != 2:
348+
print("Usage: codegen [dirname]", file=sys.stderr)
349+
sys.exit(1)
350+
perform_codegen(sys.argv[1])

codegen/compatibility.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,24 @@ def build_deprecated_datatypes_py():
5252
"""
5353

5454
# Initialize source code buffer
55-
# -----------------------------
5655
buffer = StringIO()
5756

5857
# Write warnings import
59-
# ---------------------
6058
buffer.write("import warnings\n")
6159

6260
# Write warnings filter
63-
# ---------------------
6461
# Use filter to enable DeprecationWarnings on our deprecated classes
6562
buffer.write(
6663
r"""
67-
warnings.filterwarnings('default',
68-
r'plotly\.graph_objs\.\w+ is deprecated',
64+
warnings.filterwarnings("default",
65+
r"plotly\.graph_objs\.\w+ is deprecated",
6966
DeprecationWarning)
7067
7168
7269
"""
7370
)
7471

7572
# Write deprecated class definitions
76-
# ----------------------------------
7773
for class_name, opts in DEPRECATED_DATATYPES.items():
7874
base_class_name = opts["base_type"].__name__
7975
depr_msg = build_deprecation_message(class_name, **opts)
@@ -93,7 +89,6 @@ def __init__(self, *args, **kwargs):
9389
)
9490

9591
# Return source string
96-
# --------------------
9792
return buffer.getvalue()
9893

9994

@@ -175,12 +170,10 @@ def write_deprecated_datatypes(outdir):
175170
None
176171
"""
177172
# Generate source code
178-
# --------------------
179173
datatype_source = build_deprecated_datatypes_py()
180174
filepath = opath.join(outdir, "graph_objs", "_deprecations.py")
181175

182176
# Write file
183-
# ----------
184177
write_source_py(datatype_source, filepath)
185178

186179

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy