Repository: pallets/werkzeug

Issue:
werkzeug 0.15.0: mystery "generated" module when invoking coverage
This seems to be a minimal reproduction:

(note I made a virtualenv one level up to avoid that being in the coverage output)

### `.coveragerc`

```ini
[run]
source = .
```

### `t.py`

```python
import flask

app = flask.Flask(__name__)

@app.route('/foo/<a>')
def hello(a):
    return 'world'


# pretend this is a test
with app.test_request_context():
    with app.test_client() as client:
        client.get(flask.url_for('hello', a='1'))
```

### runtime

```console
$ bash -xc 'coverage erase && coverage run t.py && coverage report && coverage xml'
+ coverage erase
+ coverage run t.py
+ coverage report
generated   NoSource: No source for code: '/tmp/t/generated'.
Aborting report output, consider using -i.
Name    Stmts   Miss  Cover
---------------------------
t.py        7      0   100%
+ coverage xml
No source for code: '/tmp/t/generated'.
Aborting report output, consider using -i.
```

### triage (?)

This appears to come from here: https://github.com/pallets/werkzeug/blob/86f7bdf6d7c005d3ab8be50226fe42edc1ce4f7c/src/werkzeug/routing.py#L1111

or, at least, that's the only instance of `'generated'` or `"generated"` in my site-packages -- so I may be guessing a bit here.

This is causing CI failures in https://github.com/asottile/git-code-debt

### workaround

A workaround appears to be setting this:

```ini
[run]
# ...
omit =
    *generated
```

in `.coveragerc`

though ideally I wouldn't have to exclude this 🤔 


Discussion:
When `Rule` generates its build code, `CodeType.filename` is set to "generated". It should probably be set to `__file__`.

https://github.com/pallets/werkzeug/blob/86f7bdf6d7c005d3ab8be50226fe42edc1ce4f7c/src/werkzeug/routing.py#L1102-L1115
I think `None` would also work (?)
@asottile I'll give that a try. Was about to write this whole thing, so I'll post anyway. :-)

---

For clarity, the code above is better as a dict:

```python
code_args = {
    "argcount": argcount,
    "nlocals": len(self.var),
    "stacksize": peak_stack + len(self.var),
    "flags": flags,
    "codestring": ops,
    "constants": tuple(self.consts),
    "names": (),
    "varnames": tuple(self.var),
    "filename": __file__,
    "name": "_build_unknown" if append_unknown else "_build",
    "firstlineno": 1,
    "lnotab": b"",
}
if not PY2:
    code_args["kwonlyargcount"] = 0
co = types.CodeType(**code_args)
```

I'm not really sure what the correct answer is. It's weird that this isn't causing the same error in Werkzeug's own coverage. How will coverage react if I tell it that the generated function is part of that module, what line do I say it starts on, what does it do when the `lnotab` table is empty, etc.?
`CodeType` doesn't work with keyword arguments. :-(
inspecting the coverage data I see two arcs, one from `(-1, 1)` and the other from `(1, -1)`, nonsense it seems

I think `werkzeug` itself avoids this due to:

https://github.com/pallets/werkzeug/blob/86f7bdf6d7c005d3ab8be50226fe42edc1ce4f7c/setup.cfg#L18-L22

Which avoids the "generated" file at `.`
Tests still pass with `None`, could you try patching that locally and see if it fixes your project's coverage?
woops, my suggestion was wrong, and I'm concerned about the test suite now 😨 

```
+ coverage run t.py
Traceback (most recent call last):
  File "t.py", line 3, in <module>
    app = flask.Flask(__name__)
  File "/tmp/venv/lib/python3.6/site-packages/flask/app.py", line 562, in __init__
    view_func=self.send_static_file
  File "/tmp/venv/lib/python3.6/site-packages/flask/app.py", line 66, in wrapper_func
    return f(self, *args, **kwargs)
  File "/tmp/venv/lib/python3.6/site-packages/flask/app.py", line 1216, in add_url_rule
    self.url_map.add(rule)
  File "/tmp/venv/lib/python3.6/site-packages/werkzeug/routing.py", line 1563, in add
    rule.bind(self)
  File "/tmp/venv/lib/python3.6/site-packages/werkzeug/routing.py", line 710, in bind
    self.compile()
  File "/tmp/venv/lib/python3.6/site-packages/werkzeug/routing.py", line 766, in compile
    self._build = self._compile_builder(False)
  File "/tmp/venv/lib/python3.6/site-packages/werkzeug/routing.py", line 1129, in _compile_builder
    return self.BuilderCompiler(self).compile(append_unknown)
  File "/tmp/venv/lib/python3.6/site-packages/werkzeug/routing.py", line 1120, in compile
    co = types.CodeType(*code_args)
TypeError: code() argument 10 must be str, not None
```

Setting it to `""` instead seems to work
:man_facepalming: I didn't actually set it to `None`.
It looks like `exec` uses `"<string>"` for that value:

```pycon
>>> s = 'print("hi")\ndef f(): pass\nprint(repr(f.__code__.co_filename))'
>>> exec(s)
hi
'<string>'
```
Yeah, I was looking at `compile` too. Does coverage skip those?
yep!

(this is in `coverage/control.py` in the released version I'm using, but here's the moved source):

https://github.com/nedbat/coveragepy/blob/28d223b4265242e08dac4bb016270dbc1b4482cb/coverage/inorout.py#L240-L245
Yeah, just found that, it skips empty or `<` prefix. Debating whether I should use something specific like `"<werkzeug routing>"` or just the empty string.
I'd go with `"<werkzeug routing>"`

Resolving patch:
diff --git a/src/werkzeug/routing.py b/src/werkzeug/routing.py
--- a/src/werkzeug/routing.py
+++ b/src/werkzeug/routing.py
@@ -110,6 +110,7 @@
 from ._compat import iteritems
 from ._compat import itervalues
 from ._compat import native_string_result
+from ._compat import PY2
 from ._compat import string_types
 from ._compat import text_type
 from ._compat import to_bytes
@@ -1099,24 +1100,22 @@ def compile(self, append_unknown=True):
                 peak_stack = max(stack + ps, peak_stack)
                 ops += self.build_op("BUILD_TUPLE", 2)
             ops += self.build_op("RETURN_VALUE")
-            code_args = [
-                argcount,
-                len(self.var),
-                peak_stack + len(self.var),
-                flags,
-                ops,
-                tuple(self.consts),
-                (),
-                tuple(self.var),
-                "generated",
-                "<builder:%r>" % self.rule.rule,
-                1,
-                b"",
+            code_args = [  # CodeType doesn't take keywords
+                argcount,  # argcount
+                len(self.var),  # nlocals
+                peak_stack + len(self.var),  # stacksize
+                flags,  # flags
+                bytes(ops),  # codestring
+                tuple(self.consts),  # constants
+                (),  # names
+                tuple(self.var),  # varnames
+                "<werkzeug routing>",  # filename, coverage ignores "<"
+                "<builder:%r>" % self.rule.rule,  # name
+                1,  # firstlineno
+                b"",  # lnotab
             ]
-            if sys.version_info >= (3,):
-                code_args[1:1] = [0]
-            else:
-                code_args[4] = str(code_args[4])
+            if not PY2:
+                code_args[1:1] = [0]  # kwonlyargcount
             co = types.CodeType(*code_args)
             fn = types.FunctionType(co, {}, None, self.argdefs)
             return fn


Test patch: