mirror of
https://github.com/zebrajr/pytorch.git
synced 2025-12-06 12:20:52 +01:00
Fixes #112633 Fixed errors relating to pydocstyle in the following files. The remaining errors are not covered in this issue. `torch/utils/dlpack.py` was not modified as the errors are relating to the function signature in the first line in the docstring which must be maintained as is for proper Sphinx interpretation. ```python def from_dlpack(ext_tensor: Any) -> 'torch.Tensor': """from_dlpack(ext_tensor) -> Tensor ..... """ ``` pydocstyle torch/utils/_contextlib.py --count before: 4 after: 0 pydocstyle torch/backends/mps/__init__.py --count before: 8 after: 1 **remaining errors** ``` torch/backends/mps/__init__.py:1 at module level: D104: Missing docstring in public package ``` pydocstyle torch/backends/xeon/run_cpu.py --count before: 13 after: 1 **remaining errors** ``` torch/backends/xeon/run_cpu.py:864 in public function `main`: D103: Missing docstring in public function ``` pydocstyle torch/backends/cpu/__init__.py --count before: 2 after: 1 **remaining errors** ``` torch/backends/cpu/__init__.py:1 at module level: D104: Missing docstring in public package ``` pydocstyle torch/utils/cpp_backtrace.py --count before: 4 after: 1 **remaining errors** ``` torch/utils/cpp_backtrace.py:1 at module level: D100: Missing docstring in public module ``` pydocstyle torch/utils/bundled_inputs.py --count before: 8 after: 1 **remaining errors** ``` torch/utils/bundled_inputs.py:1 at module level: D100: Missing docstring in public module ``` pydocstyle torch/utils/file_baton.py --count before: 8 after: 1 **remaining errors** ``` torch/utils/file_baton.py:1 at module level: D100: Missing docstring in public module ``` pydocstyle torch/utils/mobile_optimizer.py --count before: 6 after: 1 **remaining errors** ``` torch/utils/mobile_optimizer.py:8 in public class `LintCode`: D101: Missing docstring in public class ``` pydocstyle torch/backends/opt_einsum/__init__.py --count before: 7 after: 5 **remaining errors** ``` torch/backends/opt_einsum/__init__.py:1 at module level: D104: Missing docstring in public package torch/backends/opt_einsum/__init__.py:67 in public function `set_flags`: D103: Missing docstring in public function torch/backends/opt_einsum/__init__.py:77 in public function `flags`: D103: Missing docstring in public function torch/backends/opt_einsum/__init__.py:93 in public class `OptEinsumModule`: D101: Missing docstring in public class torch/backends/opt_einsum/__init__.py:94 in public method `__init__`: D107: Missing docstring in __init__ ``` pydocstyle torch/utils/_device.py --count before: 9 after: 6 **remaining errors** ``` torch/utils/_device.py:58 in public class `DeviceContext`: D101: Missing docstring in public class torch/utils/_device.py:59 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/_device.py:62 in public method `__enter__`: D105: Missing docstring in magic method torch/utils/_device.py:68 in public method `__exit__`: D105: Missing docstring in magic method torch/utils/_device.py:73 in public method `__torch_function__`: D105: Missing docstring in magic method torch/utils/_device.py:80 in public function `device_decorator`: D103: Missing docstring in public function ``` pydocstyle torch/utils/_freeze.py --count before: 15 after: 7 **remaining errors** ``` torch/utils/_freeze.py:77 in public function `indent_msg`: D103: Missing docstring in public function torch/utils/_freeze.py:89 in public class `FrozenModule`: D101: Missing docstring in public class torch/utils/_freeze.py:100 in public class `Freezer`: D101: Missing docstring in public class torch/utils/_freeze.py:101 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/_freeze.py:106 in public method `msg`: D102: Missing docstring in public method torch/utils/_freeze.py:185 in public method `get_module_qualname`: D102: Missing docstring in public method torch/utils/_freeze.py:206 in public method `compile_string`: D102: Missing docstring in public method ``` pydocstyle torch/utils/throughput_benchmark.py --count before: 25 after: 8 **remaining errors** ``` torch/utils/throughput_benchmark.py:1 at module level: D100: Missing docstring in public module torch/utils/throughput_benchmark.py:27 in public class `ExecutionStats`: D101: Missing docstring in public class torch/utils/throughput_benchmark.py:28 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/throughput_benchmark.py:33 in public method `latency_avg_ms`: D102: Missing docstring in public method torch/utils/throughput_benchmark.py:37 in public method `num_iters`: D102: Missing docstring in public method torch/utils/throughput_benchmark.py:46 in public method `total_time_seconds`: D102: Missing docstring in public method torch/utils/throughput_benchmark.py:50 in public method `__str__`: D105: Missing docstring in magic method torch/utils/throughput_benchmark.py:94 in public method `__init__`: D107: Missing docstring in __init__ ``` pydocstyle torch/utils/hooks.py --count before: 14 after: 11 **remaining errors** ``` torch/utils/hooks.py:1 at module level: D100: Missing docstring in public module torch/utils/hooks.py:23 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/hooks.py:34 in public method `remove`: D102: Missing docstring in public method torch/utils/hooks.py:44 in public method `__getstate__`: D105: Missing docstring in magic method torch/utils/hooks.py:50 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/hooks.py:64 in public method `__enter__`: D105: Missing docstring in magic method torch/utils/hooks.py:67 in public method `__exit__`: D105: Missing docstring in magic method torch/utils/hooks.py:82 in public function `warn_if_has_hooks`: D103: Missing docstring in public function torch/utils/hooks.py:103 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/hooks.py:188 in public method `setup_input_hook`: D102: Missing docstring in public method torch/utils/hooks.py:197 in public method `setup_output_hook`: D102: Missing docstring in public method ``` pydocstyle torch/utils/_traceback.py --count before: 19 after: 14 **remaining errors** ``` torch/utils/_traceback.py:47 in public function `report_compile_source_on_error`: D103: Missing docstring in public function torch/utils/_traceback.py:160 in public class `CapturedTraceback`: D101: Missing docstring in public class torch/utils/_traceback.py:163 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/_traceback.py:167 in public method `cleanup`: D102: Missing docstring in public method torch/utils/_traceback.py:170 in public method `summary`: D102: Missing docstring in public method torch/utils/_traceback.py:182 in public method `__getstate__`: D105: Missing docstring in magic method torch/utils/_traceback.py:190 in public method `extract`: D205: 1 blank line required between summary line and description (found 0) torch/utils/_traceback.py:190 in public method `extract`: D400: First line should end with a period (not 't') torch/utils/_traceback.py:213 in public method `format`: D205: 1 blank line required between summary line and description (found 0) torch/utils/_traceback.py:213 in public method `format`: D400: First line should end with a period (not 'f') torch/utils/_traceback.py:213 in public method `format`: D401: First line should be in imperative mood (perhaps 'Format', not 'Formats') torch/utils/_traceback.py:224 in public method `format_all`: D200: One-line docstring should fit on one line with quotes (found 3) torch/utils/_traceback.py:247 in private function `_extract_symbolized_tb`: D205: 1 blank line required between summary line and description (found 0) torch/utils/_traceback.py:247 in private function `_extract_symbolized_tb`: D400: First line should end with a period (not 'f') ``` pydocstyle torch/utils/mkldnn.py --count before: 28 after: 26 **remaining errors** ``` torch/utils/mkldnn.py:1 at module level: D100: Missing docstring in public module torch/utils/mkldnn.py:4 in public class `MkldnnLinear`: D101: Missing docstring in public class torch/utils/mkldnn.py:5 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:19 in public method `__getstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:23 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:29 in public method `forward`: D102: Missing docstring in public method torch/utils/mkldnn.py:75 in public class `MkldnnConv1d`: D101: Missing docstring in public class torch/utils/mkldnn.py:76 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:82 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:88 in public class `MkldnnConv2d`: D101: Missing docstring in public class torch/utils/mkldnn.py:89 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:100 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:110 in public class `MkldnnConv3d`: D101: Missing docstring in public class torch/utils/mkldnn.py:111 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:122 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:133 in public class `MkldnnBatchNorm`: D101: Missing docstring in public class torch/utils/mkldnn.py:136 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:155 in public method `__getstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:163 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:171 in public method `forward`: D102: Missing docstring in public method torch/utils/mkldnn.py:184 in public class `MkldnnPrelu`: D101: Missing docstring in public class torch/utils/mkldnn.py:185 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/mkldnn.py:190 in public method `__getstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:194 in public method `__setstate__`: D105: Missing docstring in magic method torch/utils/mkldnn.py:199 in public method `forward`: D102: Missing docstring in public method torch/utils/mkldnn.py:205 in public function `to_mkldnn`: D103: Missing docstring in public function ``` pydocstyle torch/utils/weak.py --count before: 32 after: 30 **remaining errors** ``` torch/utils/weak.py:1 at module level: D100: Missing docstring in public module torch/utils/weak.py:42 in public class `WeakIdRef`: D101: Missing docstring in public class torch/utils/weak.py:45 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/weak.py:54 in public method `__call__`: D102: Missing docstring in public method torch/utils/weak.py:61 in public method `__hash__`: D105: Missing docstring in magic method torch/utils/weak.py:64 in public method `__eq__`: D105: Missing docstring in magic method torch/utils/weak.py:84 in public class `WeakIdKeyDictionary`: D101: Missing docstring in public class torch/utils/weak.py:87 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/weak.py:131 in public method `__delitem__`: D105: Missing docstring in magic method torch/utils/weak.py:135 in public method `__getitem__`: D105: Missing docstring in magic method torch/utils/weak.py:138 in public method `__len__`: D105: Missing docstring in magic method torch/utils/weak.py:145 in public method `__repr__`: D105: Missing docstring in magic method torch/utils/weak.py:148 in public method `__setitem__`: D105: Missing docstring in magic method torch/utils/weak.py:151 in public method `copy`: D102: Missing docstring in public method torch/utils/weak.py:162 in public method `__deepcopy__`: D105: Missing docstring in magic method torch/utils/weak.py:172 in public method `get`: D102: Missing docstring in public method torch/utils/weak.py:175 in public method `__contains__`: D105: Missing docstring in magic method torch/utils/weak.py:182 in public method `items`: D102: Missing docstring in public method torch/utils/weak.py:189 in public method `keys`: D102: Missing docstring in public method torch/utils/weak.py:198 in public method `values`: D102: Missing docstring in public method torch/utils/weak.py:216 in public method `popitem`: D102: Missing docstring in public method torch/utils/weak.py:224 in public method `pop`: D102: Missing docstring in public method torch/utils/weak.py:228 in public method `setdefault`: D102: Missing docstring in public method torch/utils/weak.py:231 in public method `update`: D102: Missing docstring in public method torch/utils/weak.py:241 in public method `__ior__`: D105: Missing docstring in magic method torch/utils/weak.py:245 in public method `__or__`: D105: Missing docstring in magic method torch/utils/weak.py:252 in public method `__ror__`: D105: Missing docstring in magic method torch/utils/weak.py:262 in public method `__eq__`: D105: Missing docstring in magic method torch/utils/weak.py:276 in public method `__init__`: D107: Missing docstring in __init__ torch/utils/weak.py:280 in public method `__call__`: D102: Missing docstring in public method ``` @mikaylagawarecki @jbschlosser @svekars Pull Request resolved: https://github.com/pytorch/pytorch/pull/113311 Approved by: https://github.com/ezyang
280 lines
9.7 KiB
Python
280 lines
9.7 KiB
Python
"""
|
|
Freeze Python packages.
|
|
|
|
Freezing makes it possible to ship arbitrary Python modules as part of a C++
|
|
library. The Python source of the module is compiled to bytecode and written
|
|
to `.c` files, to be imported by Python's built-in FrozenImporter.
|
|
|
|
In a normal Python installation, FrozenImporter is only used to bootstrap the
|
|
initialization of the import machinery. Python's importers are defined in
|
|
Python (see `_bootstrap.py` and `_bootstrap_external.py`) but need to be
|
|
retrieved before any importers are available. Freezing the module bytecode
|
|
resolves this circular dependency.
|
|
|
|
This script will freeze the Python standard library. It produces two things:
|
|
- Bytecode files: A set of `.c` that define C variables containing Python bytecode.
|
|
- Main file: A `main.c` file listing all of these modules in the right form to be
|
|
consumed by FrozenImporter.
|
|
|
|
The library that wishes to these modules make them available to the local
|
|
Python instance by extending `PyImport_FrozenModules` appropriately (see
|
|
https://docs.python.org/3/c-api/import.html#c.PyImport_FrozenModules).
|
|
"""
|
|
|
|
import argparse
|
|
import functools
|
|
import itertools
|
|
import marshal
|
|
import os
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import List
|
|
import types
|
|
|
|
|
|
PATH_MARKER = "<Generated by torch::deploy>"
|
|
MAIN_INCLUDES = """#include <Python.h>
|
|
|
|
"""
|
|
|
|
MAIN_PREFIX_TEMPLATE = """
|
|
// Compiled standard library modules. These should be appended to the existing
|
|
// `PyImport_FrozenModules` that ships with CPython.
|
|
struct _frozen {}[] = {{
|
|
"""
|
|
|
|
FAKE_PREFIX = MAIN_PREFIX_TEMPLATE.format("_PyImport_FrozenModules")
|
|
|
|
MAIN_SUFFIX = """\
|
|
{0, 0, 0} /* sentinel */
|
|
};
|
|
"""
|
|
|
|
# Exclude some standard library modules to:
|
|
# 1. Slim down the final frozen lib.
|
|
# 2. Remove functionality we don't want to support.
|
|
DENY_LIST = [
|
|
# Interface to unix databases
|
|
"dbm",
|
|
# ncurses bindings (terminal interfaces)
|
|
"curses",
|
|
# Tcl/Tk GUI
|
|
"tkinter",
|
|
"tkinter",
|
|
# Tests for the standard library
|
|
"test",
|
|
"tests",
|
|
"idle_test",
|
|
"__phello__.foo.py",
|
|
# importlib frozen modules. These are already baked into CPython.
|
|
"_bootstrap.py",
|
|
"_bootstrap_external.py",
|
|
]
|
|
|
|
NUM_BYTECODE_FILES = 5
|
|
|
|
|
|
def indent_msg(fn):
|
|
@functools.wraps(fn)
|
|
def wrapper(*args, **kwargs):
|
|
args[0].indent += 1
|
|
ret = fn(*args, **kwargs)
|
|
args[0].indent -= 1
|
|
return ret
|
|
|
|
return wrapper
|
|
|
|
|
|
@dataclass
|
|
class FrozenModule:
|
|
# The fully qualified module name, e.g. 'foo.bar.baz'
|
|
module_name: str
|
|
# The name of the C variable that holds the bytecode, e.g. 'M_foo__bar__baz'
|
|
c_name: str
|
|
# The size of the C variable. Negative if this module is a package.
|
|
size: int
|
|
# The frozen bytecode
|
|
bytecode: bytes
|
|
|
|
|
|
class Freezer:
|
|
def __init__(self, verbose: bool):
|
|
self.frozen_modules: List[FrozenModule] = []
|
|
self.indent: int = 0
|
|
self.verbose: bool = verbose
|
|
|
|
def msg(self, path: Path, code: str):
|
|
if not self.verbose:
|
|
return
|
|
# P: package dir
|
|
# F: python file
|
|
# S: skipped (not a package dir)
|
|
# X: skipped (deny-listed)
|
|
# N: skipped (not a python file)
|
|
for i in range(self.indent):
|
|
print(" ", end="")
|
|
print(f"{code} {path}")
|
|
|
|
def write_bytecode(self, install_root):
|
|
"""
|
|
Write the `.c` files containing the frozen bytecode.
|
|
|
|
Shared frozen modules evenly across the files.
|
|
"""
|
|
bytecode_file_names = [
|
|
f"bytecode_{i}.c" for i in range(NUM_BYTECODE_FILES)
|
|
]
|
|
bytecode_files = [open(os.path.join(install_root, name), "w") for name in bytecode_file_names]
|
|
it = itertools.cycle(bytecode_files)
|
|
for m in self.frozen_modules:
|
|
self.write_frozen(m, next(it))
|
|
|
|
for f in bytecode_files:
|
|
f.close()
|
|
|
|
def write_main(self, install_root, oss, symbol_name):
|
|
"""Write the `main.c` file containing a table enumerating all the frozen modules."""
|
|
with open(os.path.join(install_root, "main.c"), "w") as outfp:
|
|
outfp.write(MAIN_INCLUDES)
|
|
for m in self.frozen_modules:
|
|
outfp.write(f"extern unsigned char {m.c_name}[];\n")
|
|
|
|
outfp.write(MAIN_PREFIX_TEMPLATE.format(symbol_name))
|
|
for m in self.frozen_modules:
|
|
outfp.write(f'\t{{"{m.module_name}", {m.c_name}, {m.size}}},\n')
|
|
outfp.write(MAIN_SUFFIX)
|
|
if oss:
|
|
outfp.write(FAKE_PREFIX)
|
|
outfp.write(MAIN_SUFFIX)
|
|
|
|
def write_frozen(self, m: FrozenModule, outfp):
|
|
"""Write a single frozen module's bytecode out to a C variable."""
|
|
outfp.write(f"unsigned char {m.c_name}[] = {{")
|
|
for i in range(0, len(m.bytecode), 16):
|
|
outfp.write("\n\t")
|
|
for c in bytes(m.bytecode[i : i + 16]):
|
|
outfp.write("%d," % c)
|
|
outfp.write("\n};\n")
|
|
|
|
def compile_path(self, path: Path, top_package_path: Path):
|
|
"""Entry point for compiling a Path object."""
|
|
if path.is_dir():
|
|
self.compile_package(path, top_package_path)
|
|
else:
|
|
self.compile_file(path, top_package_path)
|
|
|
|
@indent_msg
|
|
def compile_package(self, path: Path, top_package_path: Path):
|
|
"""Compile all the files within a Python package dir."""
|
|
assert path.is_dir()
|
|
if path.name in DENY_LIST:
|
|
self.msg(path, "X")
|
|
return
|
|
|
|
# Python packages are directories that have __init__.py in them.
|
|
is_package_dir = any(child.name == "__init__.py" for child in path.iterdir())
|
|
if not is_package_dir:
|
|
self.msg(path, "S")
|
|
return
|
|
|
|
self.msg(path, "P")
|
|
# Recursively compile all children in this dir
|
|
for child in path.iterdir():
|
|
self.compile_path(child, top_package_path)
|
|
|
|
def get_module_qualname(self, file_path: Path, top_package_path: Path) -> List[str]:
|
|
# `path` looks like 'Lib/foo/bar/baz.py'
|
|
|
|
# chop off 'Lib/' to get something that represents a Python module hierarchy.
|
|
# e.g. 'foo/bar/baz.py', which maps to 'foo.bar.baz'
|
|
normalized_path = file_path.relative_to(top_package_path.parent)
|
|
|
|
if normalized_path.name == "__init__.py":
|
|
# Special handling for `__init__.py`. In this case, this file
|
|
# specifies that the containing directory should be treated as a package.
|
|
# For 'foo/bar/baz/__init__.py':
|
|
# - The module name is 'baz'
|
|
module_basename = normalized_path.parent.name
|
|
# - The parent is foo.bar (need to shave off the 'baz')
|
|
module_parent = normalized_path.parent.parent.parts
|
|
else:
|
|
module_basename = normalized_path.stem
|
|
module_parent = normalized_path.parent.parts
|
|
return list(module_parent) + [module_basename]
|
|
|
|
|
|
def compile_string(self, file_content: str) -> types.CodeType:
|
|
# instead of passing in the real build time path to 'compile', we
|
|
# pass in a marker instead. This prevents the build time path being
|
|
# leaked to runtime. That path may not be available at runtime.
|
|
# Setting the path to a mark make sure it's a hard error rather
|
|
# than a flaky error when inspect module tries to retrieve python source
|
|
# code during torchscripting.
|
|
path_marker = PATH_MARKER
|
|
return compile(file_content, path_marker, "exec")
|
|
|
|
@indent_msg
|
|
def compile_file(self, path: Path, top_package_path: Path):
|
|
"""
|
|
Compile a Python source file to frozen bytecode.
|
|
|
|
Append the result to `self.frozen_modules`.
|
|
"""
|
|
assert path.is_file()
|
|
if path.suffix != ".py":
|
|
self.msg(path, "N")
|
|
return
|
|
|
|
if path.name in DENY_LIST:
|
|
self.msg(path, "X")
|
|
return
|
|
|
|
self.msg(path, "F")
|
|
module_qualname = self.get_module_qualname(path, top_package_path)
|
|
module_mangled_name = "__".join(module_qualname)
|
|
c_name = "M_" + module_mangled_name
|
|
|
|
with open(path) as src_file:
|
|
co = self.compile_string(src_file.read())
|
|
|
|
bytecode = marshal.dumps(co)
|
|
size = len(bytecode)
|
|
if path.name == '__init__.py':
|
|
# Python packages are signified by negative size.
|
|
size = -size
|
|
self.frozen_modules.append(
|
|
FrozenModule(".".join(module_qualname), c_name, size, bytecode)
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Compile py source")
|
|
parser.add_argument("paths", nargs="*", help="Paths to freeze.")
|
|
parser.add_argument("--verbose", action="store_true", help="Print debug logs")
|
|
parser.add_argument("--install-dir", "--install_dir", help="Root directory for all output files")
|
|
parser.add_argument("--oss", action="store_true", help="If it's OSS build, add a fake _PyImport_FrozenModules")
|
|
parser.add_argument(
|
|
"--symbol-name",
|
|
"--symbol_name",
|
|
help="The name of the frozen module array symbol to generate",
|
|
default="_PyImport_FrozenModules_torch",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
f = Freezer(args.verbose)
|
|
|
|
for p in args.paths:
|
|
path = Path(p)
|
|
if path.is_dir() and not Path.exists(path / '__init__.py'):
|
|
# this 'top level path p' is a standard directory containing modules,
|
|
# not a module itself
|
|
# each 'mod' could be a dir containing __init__.py or .py file
|
|
# NB: sorted to make sure this is deterministic
|
|
for mod in sorted(path.glob("*")):
|
|
f.compile_path(mod, mod)
|
|
else:
|
|
f.compile_path(path, path)
|
|
|
|
f.write_bytecode(args.install_dir)
|
|
f.write_main(args.install_dir, args.oss, args.symbol_name)
|