archaic 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- archaic/__init__.py +3 -0
- archaic/feature_class.py +210 -0
- archaic/info.py +82 -0
- archaic/predicate.py +155 -0
- archaic-0.1.0.dist-info/METADATA +14 -0
- archaic-0.1.0.dist-info/RECORD +8 -0
- archaic-0.1.0.dist-info/WHEEL +5 -0
- archaic-0.1.0.dist-info/top_level.txt +1 -0
archaic/__init__.py
ADDED
archaic/feature_class.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import arcpy
|
|
2
|
+
from typing import Any, Callable, Generic, Iterable, List, Optional, Set, TypeVar, Union
|
|
3
|
+
from archaic.info import Info
|
|
4
|
+
from archaic.predicate import to_sql
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FeatureClass(Generic[T]):
|
|
10
|
+
def __init__(self, data_path: str, **mapping: str) -> None:
|
|
11
|
+
"""Initializes the feature class. e.g.
|
|
12
|
+
|
|
13
|
+
class City:
|
|
14
|
+
objectid: int
|
|
15
|
+
city_name: str
|
|
16
|
+
shape: arcpy.PointGeometry
|
|
17
|
+
|
|
18
|
+
cities_fc = FeatureClass [City] ('world.gdb/cities')
|
|
19
|
+
|
|
20
|
+
for city in cities_fc.read():
|
|
21
|
+
print(city.city_name, city.shape.WKT)
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
data_path (str): Feature class path.
|
|
25
|
+
mapping: Custom mapping of property to field.
|
|
26
|
+
"""
|
|
27
|
+
self._data_path = data_path
|
|
28
|
+
self._mapping = mapping
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def info(self):
|
|
32
|
+
if not hasattr(self, "_info"):
|
|
33
|
+
self._info = Info[T](self)
|
|
34
|
+
return self._info
|
|
35
|
+
|
|
36
|
+
def read(
|
|
37
|
+
self,
|
|
38
|
+
filter: Union[
|
|
39
|
+
str, Callable[[T], bool], Iterable[int], Iterable[str], None
|
|
40
|
+
] = None,
|
|
41
|
+
wkid: Optional[int] = None,
|
|
42
|
+
**kwargs: Any,
|
|
43
|
+
) -> Iterable[T]:
|
|
44
|
+
"""Queries the feature class.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
filter: Where clause, lambda expression, object ids or global ids. Defaults to None.
|
|
48
|
+
wkid: Well-known id (e.g. 4326). Defaults to None.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Iterable[T]: Strongly typed items.
|
|
52
|
+
"""
|
|
53
|
+
if wkid is not None:
|
|
54
|
+
kwargs["spatial_reference"] = arcpy.SpatialReference(wkid)
|
|
55
|
+
data_path = self.info.data_path
|
|
56
|
+
fields = list(self.info.properties.values())
|
|
57
|
+
properties = self.info.properties
|
|
58
|
+
for where_clause in self._get_where_clauses_from_filter(filter):
|
|
59
|
+
with arcpy.da.SearchCursor(data_path, fields, where_clause, **kwargs) as cursor: # type: ignore
|
|
60
|
+
for row in cursor:
|
|
61
|
+
d = dict(zip(fields, row))
|
|
62
|
+
yield self._create(
|
|
63
|
+
**{p: d.get(f) if f else None for p, f in properties.items()}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def get(self, id: Union[int, str], wkid: Optional[int] = None) -> Optional[T]:
|
|
67
|
+
"""Gets an item from the feature class.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
id: Object id or global id.
|
|
71
|
+
wkid: Well-known id (e.g. 4326). Defaults to None.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Optional[T]: Strongly typed item if found.
|
|
75
|
+
"""
|
|
76
|
+
for where_clause in self._get_where_clauses_from_ids(id):
|
|
77
|
+
for item in self.read(where_clause, wkid):
|
|
78
|
+
return item
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
def insert_many(self, items: Iterable[T], **kwargs: Any) -> List[int]:
|
|
82
|
+
data_path = self.info.data_path
|
|
83
|
+
fields = list(self.info.edit_properties.values())
|
|
84
|
+
properties = self.info.edit_properties
|
|
85
|
+
with arcpy.da.InsertCursor(data_path, fields, **kwargs) as cursor: # type: ignore
|
|
86
|
+
return [
|
|
87
|
+
cursor.insertRow(self._get_values(item, properties)) for item in items
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
def insert(self, item: T) -> T:
|
|
91
|
+
return self.get(self.insert_many([item])[0]) # type: ignore
|
|
92
|
+
|
|
93
|
+
def update_where(
|
|
94
|
+
self,
|
|
95
|
+
filter: Union[str, Callable[[T], bool], Iterable[int], Iterable[str], None],
|
|
96
|
+
update: Callable[[T], Union[None, T]],
|
|
97
|
+
**kwargs: Any,
|
|
98
|
+
) -> List[int]:
|
|
99
|
+
data_path = self.info.data_path
|
|
100
|
+
fields = list(self.info.edit_properties.values())
|
|
101
|
+
properties = self.info.edit_properties
|
|
102
|
+
ids: Set[int] = set()
|
|
103
|
+
for where_clause in self._get_where_clauses_from_filter(filter):
|
|
104
|
+
with arcpy.da.UpdateCursor(data_path, fields, where_clause, **kwargs) as cursor: # type: ignore
|
|
105
|
+
for row in cursor:
|
|
106
|
+
d = dict(zip(fields, row))
|
|
107
|
+
before = self._create(
|
|
108
|
+
**{p: d.get(f) if f else None for p, f in properties.items()}
|
|
109
|
+
)
|
|
110
|
+
result = update(before)
|
|
111
|
+
after = before if result is None else result
|
|
112
|
+
cursor.updateRow(self._get_values(after, properties))
|
|
113
|
+
ids.add(self._get_oid(before))
|
|
114
|
+
return list(ids)
|
|
115
|
+
|
|
116
|
+
def update(self, items: Union[T, List[T]]) -> List[int]:
|
|
117
|
+
items = list(items) if isinstance(items, Iterable) else [items]
|
|
118
|
+
cache = {self._get_oid(x): x for x in items}
|
|
119
|
+
ids: Set[int] = set()
|
|
120
|
+
for where_clause in self._get_where_clauses_from_ids(items):
|
|
121
|
+
for id in self.update_where(
|
|
122
|
+
where_clause, lambda x: cache[self._get_oid(x)]
|
|
123
|
+
):
|
|
124
|
+
ids.add(id)
|
|
125
|
+
return list(ids)
|
|
126
|
+
|
|
127
|
+
def delete_where(self, filter: Union[str, Callable[[T], bool], None]) -> List[int]:
|
|
128
|
+
data_path = self.info.data_path
|
|
129
|
+
ids: Set[int] = set()
|
|
130
|
+
for where_clause in self._get_where_clauses_from_filter(filter):
|
|
131
|
+
with arcpy.da.UpdateCursor(data_path, self.info.oid_field, where_clause) as cursor: # type: ignore
|
|
132
|
+
for row in cursor:
|
|
133
|
+
cursor.deleteRow()
|
|
134
|
+
ids.add(row[0])
|
|
135
|
+
return list(ids)
|
|
136
|
+
|
|
137
|
+
def delete(
|
|
138
|
+
self, items: Union[T, int, str, Iterable[T], Iterable[int], Iterable[str]]
|
|
139
|
+
) -> List[int]:
|
|
140
|
+
ids: Set[int] = set()
|
|
141
|
+
for where_clause in self._get_where_clauses_from_ids(items):
|
|
142
|
+
for id in self.delete_where(where_clause):
|
|
143
|
+
ids.add(id)
|
|
144
|
+
return list(ids)
|
|
145
|
+
|
|
146
|
+
def _create(self, **kwargs: Any) -> T:
|
|
147
|
+
if self.info.has_default_constructor:
|
|
148
|
+
item = self.info.model()
|
|
149
|
+
for property in self.info.properties:
|
|
150
|
+
setattr(item, property, kwargs.get(property))
|
|
151
|
+
return item
|
|
152
|
+
return self.info.model(
|
|
153
|
+
**{k: v for k, v in kwargs.items() if k in self.info.properties}
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def _get_values(self, item: T, properties: Iterable[str]) -> List[Any]:
|
|
157
|
+
values: List[Any] = []
|
|
158
|
+
for property in properties:
|
|
159
|
+
values.append(getattr(item, property) if hasattr(item, property) else None)
|
|
160
|
+
return values
|
|
161
|
+
|
|
162
|
+
def _get_where_clauses_from_ids(
|
|
163
|
+
self, obj: Union[T, int, str, Iterable[T], Iterable[int], Iterable[str]]
|
|
164
|
+
) -> List[str]:
|
|
165
|
+
where_clauses: List[str] = []
|
|
166
|
+
ids = list(self._get_ids(obj))
|
|
167
|
+
n = 1000
|
|
168
|
+
for chunk in [ids[i : i + n] for i in range(0, len(ids), n)]:
|
|
169
|
+
first = chunk[0]
|
|
170
|
+
if isinstance(first, int):
|
|
171
|
+
where_clauses.append(
|
|
172
|
+
f"{self.info.oid_field} IN ({','.join(map(str, chunk))})"
|
|
173
|
+
)
|
|
174
|
+
elif isinstance(first, str):
|
|
175
|
+
where_clauses.append(
|
|
176
|
+
f"GlobalID IN ({','.join(map(self._quote, chunk))})"
|
|
177
|
+
)
|
|
178
|
+
return where_clauses
|
|
179
|
+
|
|
180
|
+
def _get_where_clauses_from_filter(
|
|
181
|
+
self,
|
|
182
|
+
filter: Union[str, Callable[[T], bool], Iterable[int], Iterable[str], None],
|
|
183
|
+
) -> List[str]:
|
|
184
|
+
if filter is None:
|
|
185
|
+
return [""]
|
|
186
|
+
if isinstance(filter, str):
|
|
187
|
+
return [filter]
|
|
188
|
+
if callable(filter):
|
|
189
|
+
return [to_sql(filter, self.info.properties)]
|
|
190
|
+
return self._get_where_clauses_from_ids(filter)
|
|
191
|
+
|
|
192
|
+
def _quote(self, value: Any) -> str:
|
|
193
|
+
return f"'{value}'"
|
|
194
|
+
|
|
195
|
+
def _get_ids(self, obj) -> Iterable[Union[int, str]]:
|
|
196
|
+
if isinstance(obj, int) or isinstance(obj, str):
|
|
197
|
+
yield obj
|
|
198
|
+
elif isinstance(obj, self.info.model):
|
|
199
|
+
yield self._get_oid(obj)
|
|
200
|
+
else:
|
|
201
|
+
for o in obj:
|
|
202
|
+
for id in self._get_ids(o):
|
|
203
|
+
yield id
|
|
204
|
+
|
|
205
|
+
def _get_oid(self, item) -> int:
|
|
206
|
+
if not self.info.oid_property:
|
|
207
|
+
raise TypeError(
|
|
208
|
+
f"'{self.info.model.__name__}' is missing the OID property."
|
|
209
|
+
)
|
|
210
|
+
return getattr(item, self.info.oid_property)
|
archaic/info.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import arcpy
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from inspect import signature
|
|
5
|
+
from itertools import chain
|
|
6
|
+
from types import SimpleNamespace
|
|
7
|
+
from typing import TYPE_CHECKING, Dict, Generic, Set, Type, TypeVar
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from archaic.feature_class import FeatureClass
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Info(Generic[T]):
|
|
16
|
+
def __init__(self, feature_class: "FeatureClass[T]") -> None:
|
|
17
|
+
if hasattr(feature_class, "__orig_class__"):
|
|
18
|
+
model = feature_class.__orig_class__.__args__[0] # type: ignore
|
|
19
|
+
else:
|
|
20
|
+
model = SimpleNamespace
|
|
21
|
+
|
|
22
|
+
keys = chain(
|
|
23
|
+
signature(model.__init__).parameters.keys(),
|
|
24
|
+
signature(model.__new__).parameters.keys(),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
description = arcpy.Describe(feature_class._data_path)
|
|
28
|
+
|
|
29
|
+
# Members.
|
|
30
|
+
self.model: Type[T] = model # type: ignore
|
|
31
|
+
self.has_default_constructor = len(set(keys)) == 3
|
|
32
|
+
self.data_path: str = description.catalogPath # type: ignore
|
|
33
|
+
self.oid_field: str
|
|
34
|
+
self.oid_property: str
|
|
35
|
+
self.properties: Dict[str, str] = {}
|
|
36
|
+
self.edit_properties: Dict[str, str] = {}
|
|
37
|
+
|
|
38
|
+
# Inspect the fields.
|
|
39
|
+
upper_fields: Dict[str, str] = {}
|
|
40
|
+
upper_read_only_fields: Set[str] = set()
|
|
41
|
+
for field in description.fields: # type: ignore
|
|
42
|
+
if re.match(r"^(?!\d)[\w$]+$", field.name):
|
|
43
|
+
upper_fields[field.name.upper()] = field.name
|
|
44
|
+
if field.type == "OID":
|
|
45
|
+
self.oid_field = field.name
|
|
46
|
+
elif not field.editable:
|
|
47
|
+
upper_read_only_fields.add(field.name.upper())
|
|
48
|
+
|
|
49
|
+
def resolve_fields():
|
|
50
|
+
if model == SimpleNamespace:
|
|
51
|
+
upper_field_to_property: Dict[str, str] = {
|
|
52
|
+
f.upper(): p for p, f in feature_class._mapping.items()
|
|
53
|
+
}
|
|
54
|
+
for upper_field, field in upper_fields.items():
|
|
55
|
+
property = upper_field_to_property.get(upper_field) or field
|
|
56
|
+
if upper_field == "SHAPE":
|
|
57
|
+
field = "SHAPE@"
|
|
58
|
+
yield property, field
|
|
59
|
+
else:
|
|
60
|
+
for model_type in reversed(model.mro()):
|
|
61
|
+
if hasattr(model_type, "__annotations__"):
|
|
62
|
+
for property in model_type.__annotations__:
|
|
63
|
+
field = feature_class._mapping.get(property) or property
|
|
64
|
+
upper_field = field.upper()
|
|
65
|
+
if upper_field == "SHAPE":
|
|
66
|
+
field = "SHAPE@"
|
|
67
|
+
elif upper_field.startswith("SHAPE_"):
|
|
68
|
+
field = upper_field.replace("SHAPE_", "SHAPE@")
|
|
69
|
+
elif upper_field not in upper_fields:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Field '{field}' not found in {self.data_path}."
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
field = upper_fields[upper_field]
|
|
75
|
+
yield property, field
|
|
76
|
+
|
|
77
|
+
for property, field in resolve_fields():
|
|
78
|
+
self.properties[property] = field
|
|
79
|
+
if field == self.oid_field:
|
|
80
|
+
self.oid_property = property
|
|
81
|
+
if field.upper() not in upper_read_only_fields:
|
|
82
|
+
self.edit_properties[property] = field
|
archaic/predicate.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from _ast import Attribute, BoolOp, Call, Compare, Constant, Name
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from inspect import getsource
|
|
5
|
+
from typing import Any, Callable, Dict, List, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def to_sql(predicate: Callable[[T], bool], properties: Dict[str, str]) -> str:
|
|
11
|
+
class LambdaFinder(ast.NodeVisitor):
|
|
12
|
+
def __init__(self, expression: Any) -> None:
|
|
13
|
+
super().__init__()
|
|
14
|
+
|
|
15
|
+
self.freevars: Dict[str, Any] = {}
|
|
16
|
+
|
|
17
|
+
# Check globals.
|
|
18
|
+
for name in expression.__code__.co_names:
|
|
19
|
+
if name in expression.__globals__:
|
|
20
|
+
self.freevars[name] = expression.__globals__[name]
|
|
21
|
+
|
|
22
|
+
# Capture closure variables.
|
|
23
|
+
closure = expression.__closure__
|
|
24
|
+
if closure:
|
|
25
|
+
for name, value in zip(
|
|
26
|
+
expression.__code__.co_freevars, [x.cell_contents for x in closure]
|
|
27
|
+
):
|
|
28
|
+
self.freevars[name] = value
|
|
29
|
+
|
|
30
|
+
line = getsource(expression).strip()
|
|
31
|
+
|
|
32
|
+
if line.endswith(":"):
|
|
33
|
+
line = f"{line}\n pass"
|
|
34
|
+
|
|
35
|
+
self.visit(ast.parse(line))
|
|
36
|
+
|
|
37
|
+
def visit_Lambda(self, node: ast.Lambda) -> Any: # pylint: disable-all
|
|
38
|
+
self.expression = node
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def find(expression: Any): # pylint: disable-all
|
|
42
|
+
visitor = LambdaFinder(expression)
|
|
43
|
+
return visitor.expression, visitor.freevars
|
|
44
|
+
|
|
45
|
+
class LambdaVisitor(ast.NodeVisitor):
|
|
46
|
+
def __init__(self, expression: ast.expr, freevars: Dict[str, Any]) -> None:
|
|
47
|
+
super().__init__()
|
|
48
|
+
self._expressions: List[Union[LambdaVisitor, str]] = []
|
|
49
|
+
self._freevars = freevars
|
|
50
|
+
self.visit(expression)
|
|
51
|
+
|
|
52
|
+
def visit_Attribute(self, node: Attribute) -> Any:
|
|
53
|
+
attr = node.attr
|
|
54
|
+
value: Any = node.value
|
|
55
|
+
if value.id in self._freevars:
|
|
56
|
+
self._expressions.append(
|
|
57
|
+
self._get_sql_value(getattr(self._freevars[value.id], attr))
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
self._expressions.append(properties[attr])
|
|
61
|
+
|
|
62
|
+
def visit_BoolOp(self, node: BoolOp) -> Any:
|
|
63
|
+
self._expressions.append("(")
|
|
64
|
+
expressions: List[Union[LambdaVisitor, str]] = []
|
|
65
|
+
for value in node.values:
|
|
66
|
+
expressions.append(LambdaVisitor(value, self._freevars))
|
|
67
|
+
expressions.append(self._convert_op(node.op))
|
|
68
|
+
expressions.pop()
|
|
69
|
+
self._expressions.extend(expressions)
|
|
70
|
+
self._expressions.append(")")
|
|
71
|
+
|
|
72
|
+
def visit_Call(self, node: Call) -> Any:
|
|
73
|
+
if not hasattr(node.func, "attr"):
|
|
74
|
+
self.generic_visit(node)
|
|
75
|
+
return
|
|
76
|
+
attr = node.func.attr # type: ignore
|
|
77
|
+
if attr == "startswith":
|
|
78
|
+
field_name = properties[node.func.value.attr] # type: ignore
|
|
79
|
+
self._expressions.append(
|
|
80
|
+
f"{field_name} LIKE '{self._get_value(node.args[0])}%'"
|
|
81
|
+
)
|
|
82
|
+
elif attr == "endswith":
|
|
83
|
+
field_name = properties[node.func.value.attr] # type: ignore
|
|
84
|
+
self._expressions.append(
|
|
85
|
+
f"{field_name} LIKE '%{self._get_value(node.args[0])}'"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def visit_Compare(self, node: Compare) -> Any:
|
|
89
|
+
op = node.ops[0]
|
|
90
|
+
if isinstance(op, ast.In):
|
|
91
|
+
field_name = properties[node.comparators[0].attr] # type: ignore
|
|
92
|
+
self._expressions.append(
|
|
93
|
+
f"{field_name} LIKE '%{self._get_value(node.left)}%'"
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
self._expressions.append(LambdaVisitor(node.left, self._freevars))
|
|
97
|
+
self._expressions.append(self._convert_op(node.ops[0]))
|
|
98
|
+
self._expressions.append(
|
|
99
|
+
LambdaVisitor(node.comparators[0], self._freevars)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def visit_Constant(self, node: Constant) -> Any:
|
|
103
|
+
self._expressions.append(self._get_sql_value(node.value))
|
|
104
|
+
|
|
105
|
+
def visit_Name(self, node: Name) -> Any:
|
|
106
|
+
self._expressions.append(self._get_sql_value(self._freevars[node.id]))
|
|
107
|
+
|
|
108
|
+
def _get_sql_value(self, value: Any) -> str:
|
|
109
|
+
if value is None:
|
|
110
|
+
return "NULL"
|
|
111
|
+
if isinstance(value, str):
|
|
112
|
+
return f"'{value}'"
|
|
113
|
+
if isinstance(value, datetime):
|
|
114
|
+
return f"timestamp '{value:%Y-%m-%d %H:%M:%S}'"
|
|
115
|
+
return str(value)
|
|
116
|
+
|
|
117
|
+
def _get_value(self, node: Any) -> Any:
|
|
118
|
+
return self._freevars[node.id] if isinstance(node, Name) else node.value
|
|
119
|
+
|
|
120
|
+
def _convert_op(self, op: Any) -> str:
|
|
121
|
+
if isinstance(op, ast.And):
|
|
122
|
+
return "AND"
|
|
123
|
+
if isinstance(op, ast.Or):
|
|
124
|
+
return "OR"
|
|
125
|
+
if isinstance(op, ast.Is):
|
|
126
|
+
return "IS"
|
|
127
|
+
if isinstance(op, ast.IsNot):
|
|
128
|
+
return "IS NOT"
|
|
129
|
+
if isinstance(op, ast.Eq):
|
|
130
|
+
return "="
|
|
131
|
+
if isinstance(op, ast.NotEq):
|
|
132
|
+
return "<>"
|
|
133
|
+
if isinstance(op, ast.Gt):
|
|
134
|
+
return ">"
|
|
135
|
+
if isinstance(op, ast.GtE):
|
|
136
|
+
return ">="
|
|
137
|
+
if isinstance(op, ast.Lt):
|
|
138
|
+
return "<"
|
|
139
|
+
if isinstance(op, ast.LtE):
|
|
140
|
+
return "<="
|
|
141
|
+
return type(op).__name__
|
|
142
|
+
|
|
143
|
+
def to_sql(self) -> str:
|
|
144
|
+
text = ""
|
|
145
|
+
for e in self._expressions:
|
|
146
|
+
text += e.to_sql() if isinstance(e, LambdaVisitor) else f" {e}"
|
|
147
|
+
return text
|
|
148
|
+
|
|
149
|
+
# Find the lambda expression and any free variables encapsulated in it.
|
|
150
|
+
expression, freevars = LambdaFinder.find(predicate)
|
|
151
|
+
|
|
152
|
+
# Generate a where clause.
|
|
153
|
+
where_clause = LambdaVisitor(expression, freevars).to_sql().strip()
|
|
154
|
+
|
|
155
|
+
return where_clause
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: archaic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simplifies handling of ArcPy rows
|
|
5
|
+
Author-email: Jiro Shirota <jshirota@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/jshirota/archaic
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/jshirota/archaic/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# archaic
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
archaic/__init__.py,sha256=nwSLYBqzXoFULI9Kq8Ac0Q1QpQLj8a7r1-gMn1zV8w0,63
|
|
2
|
+
archaic/feature_class.py,sha256=iUKrmFOdIt51qY1CLA8H_vMTDjmOtObt1T4rKrJdrbY,8123
|
|
3
|
+
archaic/info.py,sha256=cWwh271vWg47Z1WVd98CTx3ApkBiNb6Iat17iO-BxcU,3465
|
|
4
|
+
archaic/predicate.py,sha256=pS1iy7z0914GgW4hg40cK8_69Q2Eto9uPSfctOyLSyw,6052
|
|
5
|
+
archaic-0.1.0.dist-info/METADATA,sha256=dgin4C5wH4EK8xjSodXNkJG-ldXH9IG0Az1QgBPWNlg,504
|
|
6
|
+
archaic-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
7
|
+
archaic-0.1.0.dist-info/top_level.txt,sha256=8OZUWuQkNdNWUgch_zC_PdHqKZHLXm_e9CXi88bS4os,8
|
|
8
|
+
archaic-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
archaic
|