Skip to content

Commit

Permalink
fix: Include default data value in function signature
Browse files Browse the repository at this point in the history
Include default `data` value (an empty dict) in function signature.
We cannot use a normal empty dict because using a mutable
data type as a default argument value in a function is almost
always a bad idea. Indeed, doing this is perhaps the canonical
gotcha in Python programming. See
<https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments>
for a full description of the problem.

With the arrival of type annotations, however, we really do want to
indicate that the default value is an empty dict.  We indicate default
values everywhere else, so not doing it in this case is confusing.

The solution here is to create a superficial implementation of
`frozendict` (which really should be part of the standard library,
alongside `frozenset`). See the rejected PEP416 for why
it is not in the standard library.

Thanks to @smoh for identifying the flaw in the documentation.
  • Loading branch information
riddell-stan committed Mar 29, 2021
1 parent 9c2d50a commit 4916048
Showing 1 changed file with 10 additions and 3 deletions.
13 changes: 10 additions & 3 deletions stan/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ def default(self, obj):
return json.JSONEncoder.default(self, obj)


# superficial frozendict implementation. Only used for function signatures
class frozendict(dict):
def __setitem__(self, key, value):
raise TypeError("'frozendict' object is immutable.")


@dataclasses.dataclass(frozen=True)
class Model:
"""Stores data associated with and proxies calls to a Stan model.
Expand Down Expand Up @@ -393,14 +399,15 @@ async def go():
return asyncio.run(go())


def build(program_code: str, data: Optional[Data] = None, random_seed: Optional[int] = None) -> Model:
def build(program_code: str, data: Data = frozendict(), random_seed: Optional[int] = None) -> Model:
"""Build (compile) a Stan program.
Arguments:
program_code: Stan program code describing a Stan model.
data: A Python dictionary or mapping providing the data for the
model. Variable names are the keys and the values are their
associated values. Default is an empty dictionary.
associated values. Default is an empty dictionary, suitable
for Stan programs with no `data` block.
random_seed: Random seed, a positive integer for random number
generation. Used to make sure that results can be reproduced.
Expand All @@ -413,7 +420,7 @@ def build(program_code: str, data: Optional[Data] = None, random_seed: Optional[
"""
# `data` must be JSON-serializable in order to send to httpstan
data = json.loads(DataJSONEncoder().encode(data)) if data else {}
data = json.loads(DataJSONEncoder().encode(data))

async def go():
io = ConsoleIO()
Expand Down

0 comments on commit 4916048

Please sign in to comment.