Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# This file is part of the lib3to6 project
3# https://github.com/mbarkhau/lib3to6
4#
5# Copyright (c) 2019-2021 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
6# SPDX-License-Identifier: MIT
8import io
9import re
10import sys
11import typing as typ
12import difflib
13import logging
15import click
17from . import common
18from . import packaging
19from . import transpile
21try:
22 import pretty_traceback
24 pretty_traceback.install(envvar='ENABLE_PRETTY_TRACEBACK')
25except ImportError:
26 pass # no need to fail because of missing dev dependency
29logger = logging.getLogger("lib3to6")
32def _configure_logging(verbose: int = 0) -> None:
33 if verbose >= 2:
34 log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-17s - %(message)s"
35 log_level = logging.DEBUG
36 elif verbose == 1:
37 log_format = "%(levelname)-7s - %(message)s"
38 log_level = logging.INFO
39 else:
40 log_format = "%(levelname)-7s - %(message)s"
41 log_level = logging.INFO
43 logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
44 logger.debug("Logging configured.")
47click.disable_unicode_literals_warning = True # type: ignore[attr-defined]
50def _print_diff(source_text: str, fixed_source_text: str) -> None:
51 differ = difflib.Differ()
53 source_lines = source_text.splitlines()
54 fixed_source_lines = fixed_source_text.splitlines()
55 diff_lines = differ.compare(source_lines, fixed_source_lines)
56 if not sys.stdout.isatty():
57 click.echo("\n".join(diff_lines))
58 return
60 for line in diff_lines:
61 if line.startswith("+ "):
62 click.echo("\u001b[32m" + line + "\u001b[0m")
63 elif line.startswith("- "):
64 click.echo("\u001b[31m" + line + "\u001b[0m")
65 elif line.startswith("? "):
66 click.echo("\u001b[36m" + line + "\u001b[0m")
67 else:
68 click.echo(line)
69 print()
72__INSTALL_REQUIRES_HELP = """
73install_requires package dependencies (space separated).
74Functions as a whitelist for backported modules.
75"""
77__DEFAULT_MODE_HELP = """
78[enabled/disabled] Default transpile mode.
79To transpile some files but not others.
80"""
83@click.command()
84@click.option(
85 '-v',
86 '--verbose',
87 count=True,
88 help="Control log level. -vv for debug level.",
89)
90@click.option(
91 "--target-version",
92 default="2.7",
93 metavar="<version>",
94 help="Target version of python.",
95)
96@click.option(
97 "--diff",
98 default=False,
99 is_flag=True,
100 help="Output diff instead of transpiled source.",
101)
102@click.option(
103 "--in-place",
104 default=False,
105 is_flag=True,
106 help="Write result back to input file.",
107)
108@click.option(
109 "--install-requires",
110 default=None,
111 metavar="<packages>",
112 help=__INSTALL_REQUIRES_HELP.strip(),
113)
114@click.option(
115 "--default-mode",
116 default='enabled',
117 metavar="<mode>",
118 help=__DEFAULT_MODE_HELP.strip(),
119)
120@click.argument(
121 "source_files",
122 metavar="<source_file>",
123 nargs=-1,
124 type=click.File(mode="r"),
125)
126def main(
127 target_version : str,
128 diff : bool,
129 in_place : bool,
130 install_requires: typ.Optional[str],
131 source_files : typ.Sequence[io.TextIOWrapper],
132 default_mode : str = 'enabled',
133 verbose : int = 0,
134) -> None:
135 _configure_logging(verbose)
137 has_opt_error = False
139 if target_version and not re.match(r"[0-9]+\.[0-9]+", target_version):
140 print(f"Invalid argument --target-version={target_version}")
141 has_opt_error = True
143 if default_mode not in ('enabled', 'disabled'):
144 print(f"Invalid argument --default-mode={default_mode}")
145 print(" Must be either 'enabled' or 'disabled'")
146 has_opt_error = True
148 if not any(source_files):
149 print("No files.")
150 has_opt_error = True
152 if has_opt_error:
153 sys.exit(1)
155 cfg = packaging.eval_build_config(
156 target_version=target_version,
157 install_requires=install_requires,
158 default_mode=default_mode,
159 )
160 for src_file in source_files:
161 ctx = common.BuildContext(cfg, src_file.name)
162 source_text = src_file.read()
163 try:
164 fixed_source_text = transpile.transpile_module(ctx, source_text)
165 except common.CheckError as err:
166 loc = src_file.name
167 if err.lineno >= 0:
168 loc += "@" + str(err.lineno)
170 err.args = (loc + " - " + err.args[0],) + err.args[1:]
171 raise
173 if diff:
174 _print_diff(source_text, fixed_source_text)
175 elif in_place:
176 with io.open(src_file.name, mode="w", encoding="utf-8") as fobj:
177 fobj.write(fixed_source_text)
178 else:
179 print(fixed_source_text)
182if __name__ == '__main__':
183 # NOTE (mb 2020-07-18): click supplies the parameters
184 # pylint:disable=no-value-for-parameter
185 main()