Skip to content

Commit 1f0499a

Browse files
authored
Merge pull request #90 from digital-asset/python-fix-type-identifiers
python: Make type string identifiers more consistent with the HTTP JSON API
2 parents db4a52c + 33eb872 commit 1f0499a

30 files changed

+651
-399
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ jobs:
44
working_directory: ~/dazl
55
docker:
66
- image: rappdw/docker-java-python
7+
resource_class: medium+
78
steps:
89
- run:
910
name: Install poetry and jq

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.7.6
1+
6.8.0

python/dazl/damlast/_builtins_meta.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
from typing import Any, Optional, Sequence, Union
1111

12-
from .daml_lf_1 import Expr, Type, BuiltinFunction
13-
from ..model.types import TypeReference, ValueReference
12+
from .daml_lf_1 import BuiltinFunction, Expr, Type, ValName
13+
from .util import package_local_name
14+
from ..model.types import TypeReference
1415

1516

1617
class _BuiltinMeta(type):
@@ -60,16 +61,16 @@ def add(self, builtin: Builtin):
6061
else:
6162
raise ValueError(f'A builtin could not be registered! {builtin!r}')
6263

63-
def resolve(self, ref: 'Union[str, ValueReference, TypeReference, BuiltinFunction]') -> \
64+
def resolve(self, ref: 'Union[str, ValName, TypeReference, BuiltinFunction]') -> \
6465
'Optional[Builtin]':
6566
"""
6667
Return a :class:`Builtin` implementation for the name or reference if one is defined.
6768
"""
6869
if isinstance(ref, BuiltinFunction):
6970
# All BuiltinFunctions MUST be defined
7071
return self.by_builtin[ref]
71-
elif isinstance(ref, (ValueReference, TypeReference)):
72-
return self.by_name.get(ref.full_name_unambiguous)
72+
elif isinstance(ref, (ValName, TypeReference)):
73+
return self.by_name.get(package_local_name(ref))
7374
elif isinstance(ref, str):
7475
return self.by_name.get(ref)
7576
else:

python/dazl/damlast/daml_lf_1.py

Lines changed: 187 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
# NOTE TO IMPLEMENTORS: A future version of this file is intended to be code-generated instead of
55
# manually maintained. The makeup of this file is intentionally highly formulaic in order to
66
# facilitate a smooth transition to automatically-generated data structures.
7+
import warnings
78

89
from dataclasses import dataclass
910
from enum import Enum
1011
from io import StringIO
11-
from typing import Callable, Optional, Sequence, Union
12+
from typing import Callable, NewType, Optional, Sequence, Tuple, Union
1213

1314
from ._base import MISSING, T
14-
from ..model.types import ModuleRef, PackageId, ValueReference, TypeReference as TypeConName, TypeReference, TypeSynName
1515
from ..util.typing import safe_cast
1616

1717

@@ -22,7 +22,9 @@ class Unit:
2222
UNIT = Unit()
2323

2424

25-
# PackageRef
25+
# Reference to a package via a package identifier. The identifier is the ascii7
26+
# lowercase hex-encoded hash of the package contents found in the DAML LF Archive.
27+
PackageRef = NewType('PackageRef', str)
2628

2729

2830
class DottedName:
@@ -33,12 +35,185 @@ class DottedName:
3335
def __init__(self, segments: Sequence[str] = ()):
3436
object.__setattr__(self, 'segments', tuple(segments))
3537

38+
def __str__(self):
39+
return '.'.join(self.segments)
40+
41+
def __eq__(self, other):
42+
return isinstance(other, DottedName) and self.segments == other.segments
43+
44+
def __hash__(self):
45+
return hash(self.segments)
46+
47+
48+
class ModuleRef:
49+
"""
50+
A reference to a module.
51+
52+
In dazl 7.0.0, ModuleRef will become a `NewType(str)`, so making assumptions about the structure
53+
of this type should be avoided, and accessor methods should be instead used for callers that
54+
care about the structure of these names.
55+
"""
56+
__slots__ = '_package_id', '_module_name'
57+
58+
def __init__(self, package_id: 'PackageRef', module_name: 'DottedName'):
59+
self._package_id = PackageRef(safe_cast(str, package_id))
60+
self._module_name = safe_cast(DottedName, module_name)
61+
62+
@property
63+
def package_id(self) -> 'PackageRef':
64+
warnings.warn(
65+
"Do not use ModuleRef.package_id; use package_ref(...) instead.", DeprecationWarning)
66+
return self._package_id
67+
68+
@property
69+
def module_name(self) -> 'DottedName':
70+
warnings.warn(
71+
"Do not use ModuleRef.module_name; use module_name(...) instead.", DeprecationWarning)
72+
return self._module_name
73+
74+
def __eq__(self, other):
75+
return isinstance(other, ModuleRef) and \
76+
self._package_id == other._package_id and \
77+
self._module_name == other._module_name
78+
79+
def __lt__(self, other):
80+
return self._package_id < other.package_id or \
81+
(self._package_id == other.package_id and self._module_name < other.module_name)
82+
83+
def __le__(self, other):
84+
return self._package_id < other.package_id or \
85+
(self._package_id == other.package_id and self._module_name <= other.module_name)
86+
87+
def __gt__(self, other):
88+
return self._package_id > other.package_id or \
89+
(self._package_id == other.package_id and self._module_name > other.module_name)
90+
91+
def __ge__(self, other):
92+
return self._package_id > other.package_id or \
93+
(self._package_id == other.package_id and self._module_name >= other.module_name)
94+
95+
def __hash__(self):
96+
return hash(self._package_id) ^ hash(self._module_name)
97+
98+
def __str__(self):
99+
return f'{self._package_id}:{self._module_name}'
100+
101+
def __repr__(self):
102+
return f'ModuleRef(package_id={self._package_id!r}, ' \
103+
f'module_name={self._module_name})'
104+
105+
106+
class _Name:
107+
"""
108+
A reference by name to another object.
109+
110+
This implementation powers all of a TypeConName, TypeSynName, and ValName.
111+
112+
In dazl 7.0.0, these will become `NewType(str)`, so making assumptions about the structure of
113+
this type should be avoided, and accessor methods should be instead used for callers that care
114+
about the structure of these names.
115+
"""
116+
117+
__slots__ = '_module', '_name'
118+
119+
def __init__(self, module: 'ModuleRef', name: 'Sequence[str]'):
120+
from collections.abc import Collection
121+
if not isinstance(name, Collection):
122+
raise TypeError(f'Tuple of strings required here (got {name!r} instead)')
123+
124+
self._module = safe_cast(ModuleRef, module)
125+
self._name = tuple(name) # type: Tuple[str, ...]
126+
127+
@property
128+
def module(self) -> 'ModuleRef':
129+
warnings.warn(
130+
"Do not use Name.module; you can use module_ref(...) instead.",
131+
DeprecationWarning)
132+
return self._module
133+
134+
@property
135+
def name(self) -> 'Sequence[str]':
136+
warnings.warn(
137+
"Do not use Name.name; you can use module_local_name(...) instead.",
138+
DeprecationWarning)
139+
return self._name
140+
141+
@property
142+
def full_name(self) -> str:
143+
from .util import module_name
144+
warnings.warn(
145+
"Do not use Name.full_name; this format is no longer used, so it has no replacement.",
146+
DeprecationWarning)
147+
return f"{module_name(self)}.{'.'.join(self._name)}"
148+
149+
@property
150+
def full_name_unambiguous(self):
151+
from .util import package_local_name
152+
warnings.warn(
153+
"Do not use Name.full_name_unambiguous; use package_local_name(...) instead.",
154+
DeprecationWarning)
155+
return package_local_name(self)
156+
157+
def __eq__(self, other):
158+
return isinstance(other, type(self)) and \
159+
self._module == other._module and self._name == other._name
160+
161+
def __ne__(self, other):
162+
return not isinstance(other, type(self)) or \
163+
self._module != other._module or self._name != other._name
164+
165+
def __lt__(self, other):
166+
if not isinstance(other, _Name):
167+
raise TypeError("must compare Name to other names")
168+
169+
return self._module < other._module or \
170+
(self._module == other._module and self._name < other._name)
171+
172+
def __le__(self, other):
173+
if not isinstance(other, _Name):
174+
raise TypeError("must compare Name to other names")
175+
return self._module <= other._module or \
176+
(self._module == other._module and self._name <= other._name)
177+
178+
def __gt__(self, other):
179+
if not isinstance(other, _Name):
180+
raise TypeError("must compare Name to other names")
181+
return self._module > other._module or \
182+
(self._module == other._module and self._name > other._name)
183+
184+
def __ge__(self, other):
185+
if not isinstance(other, _Name):
186+
raise TypeError("must compare Name to other names")
187+
188+
return self._module >= other._module or \
189+
(self._module == other._module and self._name >= other._name)
190+
191+
def __hash__(self):
192+
return hash(self._module) ^ hash(self._name)
193+
194+
def __str__(self):
195+
return f"{self._module}:{'.'.join(self._name)}"
196+
197+
def __repr__(self):
198+
return f"{type(self).__name__}({str(self)!r})"
199+
200+
201+
class TypeConName(_Name):
202+
"""
203+
A reference to a type constructor.
204+
"""
205+
36206

37-
# ModuleRef
38-
# TypeConName
207+
class TypeSynName(_Name):
208+
"""
209+
A reference to a type synonym.
210+
"""
39211

40212

41-
ValName = ValueReference
213+
class ValName(_Name):
214+
"""
215+
A reference to a value.
216+
"""
42217

43218

44219
@dataclass(frozen=True)
@@ -162,10 +337,10 @@ class Var:
162337
args: 'Sequence[Type]'
163338

164339
class Con:
165-
tycon: 'TypeReference'
340+
tycon: 'TypeConName'
166341
args: 'Sequence[Type]'
167342

168-
def __init__(self, tycon: 'TypeReference', args: 'Sequence[Type]'):
343+
def __init__(self, tycon: 'TypeConName', args: 'Sequence[Type]'):
169344
self.tycon = tycon
170345
self.args = tuple(args)
171346

@@ -413,7 +588,7 @@ def numeric(self) -> 'Optional[str]':
413588
Serialization of number with precision 38 and scale between 0 and 37
414589
415590
Must be a string that matched
416-
`-?([0-1]\d*|0)\.(\d*)
591+
`-?([0-1]\\d*|0)\\.(\\d*)
417592
418593
The number of decimal digits indicate the scale of the number.
419594
"""
@@ -504,7 +679,7 @@ def __init__(self, tycon: 'Type.Con', variant_con: str, variant_arg: 'Expr'):
504679

505680
@dataclass(frozen=True)
506681
class EnumCon:
507-
tycon: 'TypeReference' # Always fully applied
682+
tycon: 'TypeConName' # Always fully applied
508683
enum_con: str
509684

510685
@dataclass(frozen=True)
@@ -991,7 +1166,7 @@ def __init__(self, con, variant, binder):
9911166

9921167
@dataclass(frozen=True)
9931168
class Enum:
994-
con: TypeReference
1169+
con: TypeConName
9951170
constructor: str
9961171

9971172
@dataclass(frozen=True)
@@ -1858,5 +2033,5 @@ class PackageMetadata:
18582033

18592034
@dataclass(frozen=True)
18602035
class Archive:
1861-
hash: 'PackageId'
2036+
hash: 'PackageRef'
18622037
package: 'Package'

python/dazl/damlast/evaluate.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33

44
from datetime import datetime, date, timedelta, timezone
55
from decimal import Decimal
6-
from typing import Any, Mapping
6+
from typing import Any, Mapping, TYPE_CHECKING
77

88
from .. import LOG
99
from ..model.core import Party
10-
from ..model.types_store import PackageStore
1110
from ..util.prim_types import frozendict
1211
from .daml_lf_1 import *
12+
from .util import package_local_name
13+
14+
15+
if TYPE_CHECKING:
16+
from ..model.types import PackageStore
1317

1418

1519
class Evaluator:
@@ -111,7 +115,7 @@ def eval_var(self, var: str) -> Any:
111115
return self.eval_Expr(expr)
112116

113117
def eval_val(self, val: 'ValName') -> 'Any':
114-
if val.full_name_unambiguous == 'DA.Internal.Prelude:concat':
118+
if package_local_name(val) == 'DA.Internal.Prelude:concat':
115119
return PartialFunction(lambda a: PartialFunction(Evaluator.concat))
116120
value = self.store.resolve_value_reference(val)
117121
if value is None:

python/dazl/damlast/expand.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from .builtins import builtins
88
from .daml_lf_1 import Block, Expr, Type, ValName
9+
from .util import package_local_name
910
from .visitor import IdentityVisitor
1011
from ..model.types_store import PackageStore
1112

@@ -67,13 +68,13 @@ def visit_expr_val(self, val: 'ValName') -> 'Expr':
6768
builtin = builtins.resolve(val)
6869
if builtin is not None:
6970
return Expr(val=val)
70-
if self.always_expand or val.name[0][0] == '$' or val.full_name_unambiguous == 'DA.Internal.Template:toParties':
71+
if self.always_expand or val.name[0][0] == '$' or package_local_name(val) == 'DA.Internal.Template:toParties':
7172
val_expr = self.resolve_val(val)
7273
if val_expr is not None:
7374
child = self.without_val(val)
7475
return child.visit_expr(val_expr)
7576
else:
76-
print(f'Failed to resolve {val.full_name_unambiguous} against blacklist {self.val_blacklist}')
77+
print(f'Failed to resolve {package_local_name(val)} against blacklist {self.val_blacklist}')
7778

7879
return super(ExpandVisitor, self).visit_expr_val(val)
7980

python/dazl/damlast/pb_parse.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33

44

55
from .daml_lf_1 import *
6-
from ..model.types import ModuleRef, PackageId, TypeReference
76

87

98
# noinspection PyPep8Naming,PyMethodMayBeStatic
109
class ProtobufParser:
11-
def __init__(self, current_package: 'PackageId'):
10+
def __init__(self, current_package: 'PackageRef'):
1211
from typing import List
1312
self.current_package = current_package
1413
self.interned_strings = [] # type: List[str]
@@ -24,17 +23,17 @@ def parse_ModuleRef(self, pb) -> 'Optional[ModuleRef]':
2423
if sum_name is None:
2524
return None
2625
elif sum_name == 'self':
27-
return ModuleRef(self.current_package, module_name.segments)
26+
return ModuleRef(self.current_package, module_name)
2827
elif sum_name == 'package_id_str':
29-
return ModuleRef(pb.package_ref.package_id_str, module_name.segments)
28+
return ModuleRef(pb.package_ref.package_id_str, module_name)
3029
elif sum_name == 'package_id_interned_str':
3130
return ModuleRef(
32-
self.interned_strings[pb.package_ref.package_id_interned_str], module_name.segments)
31+
self.interned_strings[pb.package_ref.package_id_interned_str], module_name)
3332
else:
3433
raise ValueError(f'unknown sum type value: {sum_name!r}')
3534

36-
def parse_TypeConName(self, pb) -> 'TypeReference':
37-
return TypeReference(
35+
def parse_TypeConName(self, pb) -> 'TypeConName':
36+
return TypeConName(
3837
self.parse_ModuleRef(pb.module),
3938
self._resolve_dotted_name(pb.name_dname, pb.name_interned_dname).segments)
4039

0 commit comments

Comments
 (0)