diff --git a/docs/Gemfile b/docs/Gemfile
new file mode 100644
index 000000000..4e2737b80
--- /dev/null
+++ b/docs/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+source "https://rubygems.org"
+
+gem "webrick"
+gem "github-pages", "~> 226", group: :jekyll_plugins
diff --git a/docs/_config.yml b/docs/_config.yml
index f8b905314..68ef1203c 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,6 +1,12 @@
theme: jekyll-theme-minimal
+google_analytics: G-G896Q7VERJ
+
defaults:
+ - scope:
+ path: ""
+ values:
+ layout: default
- scope:
path: developers
values:
diff --git a/docs/_layouts/dev_guide.html b/docs/_layouts/dev_guide.html
index 6b13aa2ae..0010fa1ae 100644
--- a/docs/_layouts/dev_guide.html
+++ b/docs/_layouts/dev_guide.html
@@ -66,6 +66,8 @@
{{ site.title | default: site.github.repo
Style guide
Tools
+
+ Debug documentation
{% if site.github.is_project_page %}
diff --git a/docs/developers/debug_docs.md b/docs/developers/debug_docs.md
new file mode 100644
index 000000000..e98f8bd5f
--- /dev/null
+++ b/docs/developers/debug_docs.md
@@ -0,0 +1,53 @@
+# How to setup environment for previewing changes to documentation
+
+
+
+The pytype documentation is in markdown format.
+
+github uses [jekyll](https://jekyllrb.com/docs/) to render these pages.
+
+## Prerequisites
+
+Install/update ruby and bundler
+
+```shell
+ruby -v # should be greater than 3.0.0
+```
+
+If it doesn't exist or is too old
+
+```shell
+sudo apt-get install ruby-full
+```
+
+Add path to your .bashrc
+
+```shell
+echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
+echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
+echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
+source ~/.bashrc
+```
+
+Install packages required for jekyll
+
+```shell
+gem install jekyll bundler webrick
+gem update jekyll
+bundle install
+```
+
+## Start jekyll locally
+
+```shell
+cd docs # you'll need to be in the pytype/docs directory
+
+bundle exec jekyll serve --watch
+```
+
+The `--watch` flag forces jekyll to look for changes to your source files and
+reload the server if it detects any changes.
+
+You can view your webpages by navigating to
+[http://localhost:4000](http://localhost:4000) like
+[http://localhost:4000/developers/index.html](http://localhost:4000/developers/index.html)
diff --git a/pytype/abstract/function.py b/pytype/abstract/function.py
index 7760cffc2..9885c10f3 100644
--- a/pytype/abstract/function.py
+++ b/pytype/abstract/function.py
@@ -902,7 +902,7 @@ def match_all_args(ctx, node, func, args):
else:
raise AssertionError(
f"Mismatched parameter {arg_name} not found in passed_args"
- ) from e
+ ) from e
else:
# This is not an InvalidParameters error.
raise
diff --git a/pytype/ast/debug.py b/pytype/ast/debug.py
index 54f1e0149..27e073d83 100644
--- a/pytype/ast/debug.py
+++ b/pytype/ast/debug.py
@@ -44,7 +44,7 @@ def _format(node, level=0):
elif isinstance(node, list):
lines = ["["]
lines.extend(indent * (level + 2) + _format(x, level + 2) + ","
- for x in node)
+ for x in node)
if len(lines) > 1:
lines.append(indent * (level + 1) + "]")
else:
diff --git a/pytype/errors.py b/pytype/errors.py
index 1d8ff6177..634e999f1 100644
--- a/pytype/errors.py
+++ b/pytype/errors.py
@@ -1086,9 +1086,8 @@ def _print_params_helper(self, param_or_params):
if isinstance(param_or_params, abstract.BaseValue):
return self._print_as_expected_type(param_or_params)
else:
- return (
- f"[{', '.join(self._print_params_helper(p) for p in param_or_params)}]"
- )
+ return "[{}]".format(
+ ", ".join(self._print_params_helper(p) for p in param_or_params))
def wrong_annotation_parameter_count(
self, stack, annot: abstract.BaseValue,
@@ -1290,7 +1289,7 @@ def annotation_type_mismatch(
# ensures that we print all of the options when 'Any' is among them.
# We don't need to print this if there is only 1 unique type.
print_types = {self._print_as_actual_type(v, literal=literal)
- for v in binding.variable.data}
+ for v in binding.variable.data}
if len(print_types) > 1:
details += ("\nIn assignment of type: "
f"{self._join_printed_types(print_types)}")
diff --git a/pytype/preprocess.py b/pytype/preprocess.py
index 004adcbcd..ad5b69f48 100644
--- a/pytype/preprocess.py
+++ b/pytype/preprocess.py
@@ -35,9 +35,9 @@ def augment_annotations(src):
if visitor.annotation_lines:
lines = src.split("\n")
for i in visitor.annotation_lines:
- # No need to preserve comments, users should never see the transformed
- # source code.
- line, *_ = lines[i].split("#", 1)
- lines[i] = line + " = ..."
+ # Preserve comments, as they may be pytype directives. We don't bother to
+ # keep the formatting, since users never see the transformed source code.
+ line, mark, comment = lines[i].partition("#")
+ lines[i] = line + " = ..." + mark + comment
src = "\n".join(lines)
return src
diff --git a/pytype/pyi/evaluator.py b/pytype/pyi/evaluator.py
index 0825f0b68..7e57c5bb7 100644
--- a/pytype/pyi/evaluator.py
+++ b/pytype/pyi/evaluator.py
@@ -39,8 +39,7 @@ def _convert(node):
elif isinstance(node, ast3.Set):
return set(map(_convert, node.elts))
elif isinstance(node, ast3.Dict):
- return {_convert(k): _convert(v)
- for k, v in zip(node.keys, node.values)}
+ return {_convert(k): _convert(v) for k, v in zip(node.keys, node.values)}
elif isinstance(node, ast3.NameConstant):
return node.value
elif isinstance(node, ast3.Name):
diff --git a/pytype/pytd/booleq.py b/pytype/pytd/booleq.py
index 6e028684c..5b8b5079a 100644
--- a/pytype/pytd/booleq.py
+++ b/pytype/pytd/booleq.py
@@ -454,7 +454,7 @@ def _iter_implications(self):
def _get_nonfalse_values(self, var):
return {value for value, implication in self.implications[var].items()
- if implication is not FALSE}
+ if implication is not FALSE}
def _get_first_approximation(self):
"""Get all (variable, value) combinations to consider.
diff --git a/pytype/tests/test_base.py b/pytype/tests/test_base.py
index 73968a438..47e12f3d1 100644
--- a/pytype/tests/test_base.py
+++ b/pytype/tests/test_base.py
@@ -321,7 +321,7 @@ def assertOnlyHasReturnType(self, func, t):
for sig in func.signatures)
self.assertEqual(t, ret,
"Return type {!r} != {!r}".format(pytd_utils.Print(t),
- pytd_utils.Print(ret)))
+ pytd_utils.Print(ret)))
def assertHasReturnType(self, func, t):
"""Test that a given return type is present. Ignore extras."""
@@ -330,11 +330,11 @@ def assertHasReturnType(self, func, t):
if isinstance(ret, pytd.UnionType):
self.assertIn(t, ret.type_list,
"Return type {!r} not found in {!r}".format(
- pytd_utils.Print(t), pytd_utils.Print(ret)))
+ pytd_utils.Print(t), pytd_utils.Print(ret)))
else:
self.assertEqual(t, ret,
"Return type {!r} != {!r}".format(pytd_utils.Print(ret),
- pytd_utils.Print(t)))
+ pytd_utils.Print(t)))
def assertHasAllReturnTypes(self, func, types):
"""Test that all given return types are present. Ignore extras."""
diff --git a/pytype/tests/test_disables.py b/pytype/tests/test_disables.py
index fb8722b92..05c7ea871 100644
--- a/pytype/tests/test_disables.py
+++ b/pytype/tests/test_disables.py
@@ -210,6 +210,13 @@ def foo(x): pass
c = foo(a, b.i) # pytype: disable=attribute-error # pytype: disable=wrong-arg-count
""")
+ def test_bare_annotation(self):
+ self.Check("""
+ from typing import AnyStr
+ def f():
+ x: AnyStr # pytype: disable=invalid-annotation
+ """)
+
class AttributeErrorDisableTest(test_base.BaseTest):
"""Test attribute-error disabling."""
diff --git a/pytype/tests/test_utils.py b/pytype/tests/test_utils.py
index 2dd04db7a..237de8232 100644
--- a/pytype/tests/test_utils.py
+++ b/pytype/tests/test_utils.py
@@ -278,7 +278,7 @@ def _format_error(line, code, mark=None):
exp = _format_error(error.lineno, code, mark)
actual = _format_error(error.lineno, error.name)
self._fail(
- f"Error does not match:\nExpected: {exp}\nActual: {actual}"
+ f"Error does not match:\nExpected: {exp}\nActual: {actual}"
)
else:
self._fail(f"Unexpected error:\n{error}")
diff --git a/pytype/tools/analyze_project/pytype_runner_test.py b/pytype/tools/analyze_project/pytype_runner_test.py
index a35b464a4..503966896 100644
--- a/pytype/tools/analyze_project/pytype_runner_test.py
+++ b/pytype/tools/analyze_project/pytype_runner_test.py
@@ -575,7 +575,7 @@ def test_cycle(self):
action=Action.INFER,
input='bar.py',
deps=' | {} {}'.format(os.path.join(runner.pyi_dir, 'bar.pyi-1'),
- os.path.join(runner.pyi_dir, 'foo.pyi-1')),
+ os.path.join(runner.pyi_dir, 'foo.pyi-1')),
imports=os.path.join(runner.imports_dir, 'bar.imports'),
module='bar'))
self.assertBuildStatementMatches(body[9:], ExpectedBuildStatement(
@@ -583,7 +583,7 @@ def test_cycle(self):
action=Action.CHECK,
input='foo.py',
deps=' | {} {}'.format(os.path.join(runner.pyi_dir, 'bar.pyi'),
- os.path.join(runner.pyi_dir, 'foo.pyi-1')),
+ os.path.join(runner.pyi_dir, 'foo.pyi-1')),
imports=os.path.join(runner.imports_dir, 'foo.imports'),
module='foo'))
@@ -617,7 +617,7 @@ def test_cycle_with_extra_action(self):
action=Action.CHECK,
input='foo.py',
deps=' | {} {}'.format(os.path.join(runner.pyi_dir, 'foo.pyi-1'),
- os.path.join(runner.pyi_dir, 'bar.pyi-1')),
+ os.path.join(runner.pyi_dir, 'bar.pyi-1')),
imports=os.path.join(runner.imports_dir, 'foo.imports'),
module='foo'))