@suss/adapter-python 0.29.0 → 0.30.0
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.
- package/dist/index.cjs +86 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +86 -1
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -1887,6 +1887,79 @@ function assignedValue(right) {
|
|
|
1887
1887
|
const first = fields(right, "subscript")[0];
|
|
1888
1888
|
return outer?.text === "Annotated" && first !== void 0 ? first : right;
|
|
1889
1889
|
}
|
|
1890
|
+
function sideBesidesNone(node) {
|
|
1891
|
+
const left = field(node, "left");
|
|
1892
|
+
const right = field(node, "right");
|
|
1893
|
+
if (right?.type === "none") {
|
|
1894
|
+
return left;
|
|
1895
|
+
}
|
|
1896
|
+
if (left?.type === "none") {
|
|
1897
|
+
return right;
|
|
1898
|
+
}
|
|
1899
|
+
return null;
|
|
1900
|
+
}
|
|
1901
|
+
function annotatedClass(node) {
|
|
1902
|
+
if (node === void 0) {
|
|
1903
|
+
return null;
|
|
1904
|
+
}
|
|
1905
|
+
if (node.type === "identifier" || node.type === "string") {
|
|
1906
|
+
return node;
|
|
1907
|
+
}
|
|
1908
|
+
if (node.type === "type") {
|
|
1909
|
+
return annotatedClass(children(node)[0]);
|
|
1910
|
+
}
|
|
1911
|
+
if (node.type === "generic_type") {
|
|
1912
|
+
const parameter = children(node).find(
|
|
1913
|
+
(child) => child.type === "type_parameter"
|
|
1914
|
+
);
|
|
1915
|
+
return annotatedClass(
|
|
1916
|
+
parameter === void 0 ? void 0 : children(parameter)[0]
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
if (node.type === "binary_operator") {
|
|
1920
|
+
return annotatedClass(sideBesidesNone(node) ?? void 0);
|
|
1921
|
+
}
|
|
1922
|
+
return null;
|
|
1923
|
+
}
|
|
1924
|
+
function classReferenceKey(emitter, node) {
|
|
1925
|
+
if (node.type === "identifier") {
|
|
1926
|
+
return valueKey(emitter, node);
|
|
1927
|
+
}
|
|
1928
|
+
const written = stringLiteralValue(node);
|
|
1929
|
+
if (written === null) {
|
|
1930
|
+
return null;
|
|
1931
|
+
}
|
|
1932
|
+
return nameKey(emitter.filePath, emitter.enclosing, written);
|
|
1933
|
+
}
|
|
1934
|
+
function firstPositional(call) {
|
|
1935
|
+
const args = field(call, "arguments");
|
|
1936
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
1937
|
+
if (argument.type !== "keyword_argument") {
|
|
1938
|
+
return argument;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return null;
|
|
1942
|
+
}
|
|
1943
|
+
function fieldClassKey(emitter, assignment2, call) {
|
|
1944
|
+
const annotation = field(assignment2, "type");
|
|
1945
|
+
const annotated = annotation === null ? null : annotatedClass(annotation);
|
|
1946
|
+
if (annotated !== null) {
|
|
1947
|
+
return classReferenceKey(emitter, annotated);
|
|
1948
|
+
}
|
|
1949
|
+
const first = firstPositional(call);
|
|
1950
|
+
return first === null ? null : classReferenceKey(emitter, first);
|
|
1951
|
+
}
|
|
1952
|
+
function emitFieldCall(emitter, classKey, name, assignment2, value) {
|
|
1953
|
+
if (value.type !== "call") {
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
const callee = field(value, "function");
|
|
1957
|
+
const target = callee === null ? null : fieldClassKey(emitter, assignment2, value);
|
|
1958
|
+
if (callee === null || target === null) {
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
add(emitter, "fieldCall", classKey, name, valueKey(emitter, callee), target);
|
|
1962
|
+
}
|
|
1890
1963
|
function declaredBy(statement) {
|
|
1891
1964
|
if (statement.type === "decorated_definition") {
|
|
1892
1965
|
return field(statement, "definition") ?? statement;
|
|
@@ -1933,6 +2006,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1933
2006
|
left.text,
|
|
1934
2007
|
valueKey(emitter, right)
|
|
1935
2008
|
);
|
|
2009
|
+
emitFieldCall(emitter, classKey, left.text, member, right);
|
|
1936
2010
|
}
|
|
1937
2011
|
continue;
|
|
1938
2012
|
}
|
|
@@ -5363,7 +5437,12 @@ function matchedChains(calls, options) {
|
|
|
5363
5437
|
}
|
|
5364
5438
|
const pattern = resolvedMethodPattern(options, callee);
|
|
5365
5439
|
if (pattern !== void 0) {
|
|
5366
|
-
|
|
5440
|
+
const returnedOnly = chain.last === chain.root;
|
|
5441
|
+
matched.push({
|
|
5442
|
+
chain,
|
|
5443
|
+
pattern,
|
|
5444
|
+
operation: returnedOnly ? null : chain.operation
|
|
5445
|
+
});
|
|
5367
5446
|
}
|
|
5368
5447
|
}
|
|
5369
5448
|
return matched;
|
|
@@ -7675,6 +7754,12 @@ function emitModelQueryFacts(db, packs) {
|
|
|
7675
7754
|
String(entry.argument)
|
|
7676
7755
|
]);
|
|
7677
7756
|
}
|
|
7757
|
+
for (const relationship of model.relationships ?? []) {
|
|
7758
|
+
db.add("associationConstructor", [
|
|
7759
|
+
relationship.module,
|
|
7760
|
+
relationship.name
|
|
7761
|
+
]);
|
|
7762
|
+
}
|
|
7678
7763
|
}
|
|
7679
7764
|
}
|
|
7680
7765
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -170,6 +170,20 @@ interface PyModelQueries {
|
|
|
170
170
|
entryMethods: PyModelEntryMethod[];
|
|
171
171
|
/** Functions the library exports that do the same, called on their own rather than read off a session. */
|
|
172
172
|
entryFunctions: PyModelEntryFunction[];
|
|
173
|
+
/** The library's own constructors for a field that reaches another model. */
|
|
174
|
+
relationships?: PyRelationshipConstructor[];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* `items: list[Item] = Relationship(...)`: the callable a model's field
|
|
178
|
+
* is given to say it reaches another model, and where it comes from. A
|
|
179
|
+
* project function spelled the same way is somebody else's, so the
|
|
180
|
+
* import is what settles a match.
|
|
181
|
+
*/
|
|
182
|
+
interface PyRelationshipConstructor {
|
|
183
|
+
/** The module it is imported from, `sqlmodel` or `sqlalchemy.orm`. */
|
|
184
|
+
module: string;
|
|
185
|
+
/** Its name, `Relationship` or `relationship`. */
|
|
186
|
+
name: string;
|
|
173
187
|
}
|
|
174
188
|
/** `session.get(User, id)`: the method, and where the class is written. */
|
|
175
189
|
interface PyModelEntryMethod {
|
package/dist/index.d.ts
CHANGED
|
@@ -170,6 +170,20 @@ interface PyModelQueries {
|
|
|
170
170
|
entryMethods: PyModelEntryMethod[];
|
|
171
171
|
/** Functions the library exports that do the same, called on their own rather than read off a session. */
|
|
172
172
|
entryFunctions: PyModelEntryFunction[];
|
|
173
|
+
/** The library's own constructors for a field that reaches another model. */
|
|
174
|
+
relationships?: PyRelationshipConstructor[];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* `items: list[Item] = Relationship(...)`: the callable a model's field
|
|
178
|
+
* is given to say it reaches another model, and where it comes from. A
|
|
179
|
+
* project function spelled the same way is somebody else's, so the
|
|
180
|
+
* import is what settles a match.
|
|
181
|
+
*/
|
|
182
|
+
interface PyRelationshipConstructor {
|
|
183
|
+
/** The module it is imported from, `sqlmodel` or `sqlalchemy.orm`. */
|
|
184
|
+
module: string;
|
|
185
|
+
/** Its name, `Relationship` or `relationship`. */
|
|
186
|
+
name: string;
|
|
173
187
|
}
|
|
174
188
|
/** `session.get(User, id)`: the method, and where the class is written. */
|
|
175
189
|
interface PyModelEntryMethod {
|
package/dist/index.js
CHANGED
|
@@ -1839,6 +1839,79 @@ function assignedValue(right) {
|
|
|
1839
1839
|
const first = fields(right, "subscript")[0];
|
|
1840
1840
|
return outer?.text === "Annotated" && first !== void 0 ? first : right;
|
|
1841
1841
|
}
|
|
1842
|
+
function sideBesidesNone(node) {
|
|
1843
|
+
const left = field(node, "left");
|
|
1844
|
+
const right = field(node, "right");
|
|
1845
|
+
if (right?.type === "none") {
|
|
1846
|
+
return left;
|
|
1847
|
+
}
|
|
1848
|
+
if (left?.type === "none") {
|
|
1849
|
+
return right;
|
|
1850
|
+
}
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
function annotatedClass(node) {
|
|
1854
|
+
if (node === void 0) {
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
if (node.type === "identifier" || node.type === "string") {
|
|
1858
|
+
return node;
|
|
1859
|
+
}
|
|
1860
|
+
if (node.type === "type") {
|
|
1861
|
+
return annotatedClass(children(node)[0]);
|
|
1862
|
+
}
|
|
1863
|
+
if (node.type === "generic_type") {
|
|
1864
|
+
const parameter = children(node).find(
|
|
1865
|
+
(child) => child.type === "type_parameter"
|
|
1866
|
+
);
|
|
1867
|
+
return annotatedClass(
|
|
1868
|
+
parameter === void 0 ? void 0 : children(parameter)[0]
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
if (node.type === "binary_operator") {
|
|
1872
|
+
return annotatedClass(sideBesidesNone(node) ?? void 0);
|
|
1873
|
+
}
|
|
1874
|
+
return null;
|
|
1875
|
+
}
|
|
1876
|
+
function classReferenceKey(emitter, node) {
|
|
1877
|
+
if (node.type === "identifier") {
|
|
1878
|
+
return valueKey(emitter, node);
|
|
1879
|
+
}
|
|
1880
|
+
const written = stringLiteralValue(node);
|
|
1881
|
+
if (written === null) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
return nameKey(emitter.filePath, emitter.enclosing, written);
|
|
1885
|
+
}
|
|
1886
|
+
function firstPositional(call) {
|
|
1887
|
+
const args = field(call, "arguments");
|
|
1888
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
1889
|
+
if (argument.type !== "keyword_argument") {
|
|
1890
|
+
return argument;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return null;
|
|
1894
|
+
}
|
|
1895
|
+
function fieldClassKey(emitter, assignment2, call) {
|
|
1896
|
+
const annotation = field(assignment2, "type");
|
|
1897
|
+
const annotated = annotation === null ? null : annotatedClass(annotation);
|
|
1898
|
+
if (annotated !== null) {
|
|
1899
|
+
return classReferenceKey(emitter, annotated);
|
|
1900
|
+
}
|
|
1901
|
+
const first = firstPositional(call);
|
|
1902
|
+
return first === null ? null : classReferenceKey(emitter, first);
|
|
1903
|
+
}
|
|
1904
|
+
function emitFieldCall(emitter, classKey, name, assignment2, value) {
|
|
1905
|
+
if (value.type !== "call") {
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
const callee = field(value, "function");
|
|
1909
|
+
const target = callee === null ? null : fieldClassKey(emitter, assignment2, value);
|
|
1910
|
+
if (callee === null || target === null) {
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
add(emitter, "fieldCall", classKey, name, valueKey(emitter, callee), target);
|
|
1914
|
+
}
|
|
1842
1915
|
function declaredBy(statement) {
|
|
1843
1916
|
if (statement.type === "decorated_definition") {
|
|
1844
1917
|
return field(statement, "definition") ?? statement;
|
|
@@ -1885,6 +1958,7 @@ function emitClassFacts(emitter, cls) {
|
|
|
1885
1958
|
left.text,
|
|
1886
1959
|
valueKey(emitter, right)
|
|
1887
1960
|
);
|
|
1961
|
+
emitFieldCall(emitter, classKey, left.text, member, right);
|
|
1888
1962
|
}
|
|
1889
1963
|
continue;
|
|
1890
1964
|
}
|
|
@@ -5336,7 +5410,12 @@ function matchedChains(calls, options) {
|
|
|
5336
5410
|
}
|
|
5337
5411
|
const pattern = resolvedMethodPattern(options, callee);
|
|
5338
5412
|
if (pattern !== void 0) {
|
|
5339
|
-
|
|
5413
|
+
const returnedOnly = chain.last === chain.root;
|
|
5414
|
+
matched.push({
|
|
5415
|
+
chain,
|
|
5416
|
+
pattern,
|
|
5417
|
+
operation: returnedOnly ? null : chain.operation
|
|
5418
|
+
});
|
|
5340
5419
|
}
|
|
5341
5420
|
}
|
|
5342
5421
|
return matched;
|
|
@@ -7687,6 +7766,12 @@ function emitModelQueryFacts(db, packs) {
|
|
|
7687
7766
|
String(entry.argument)
|
|
7688
7767
|
]);
|
|
7689
7768
|
}
|
|
7769
|
+
for (const relationship of model.relationships ?? []) {
|
|
7770
|
+
db.add("associationConstructor", [
|
|
7771
|
+
relationship.module,
|
|
7772
|
+
relationship.name
|
|
7773
|
+
]);
|
|
7774
|
+
}
|
|
7690
7775
|
}
|
|
7691
7776
|
}
|
|
7692
7777
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/adapter-python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Read a Python codebase with suss and check what each FastAPI or Flask route returns against the clients, specs and infrastructure that depend on it.",
|
|
6
6
|
"keywords": [
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@suss/behavioral-ir": "0.
|
|
53
|
-
"@suss/datalog": "0.
|
|
54
|
-
"@suss/extractor": "0.
|
|
55
|
-
"@suss/ir-core": "0.
|
|
56
|
-
"@suss/resolution": "0.
|
|
57
|
-
"@suss/sql": "0.
|
|
58
|
-
"@suss/values": "0.
|
|
52
|
+
"@suss/behavioral-ir": "0.30.0",
|
|
53
|
+
"@suss/datalog": "0.30.0",
|
|
54
|
+
"@suss/extractor": "0.30.0",
|
|
55
|
+
"@suss/ir-core": "0.30.0",
|
|
56
|
+
"@suss/resolution": "0.30.0",
|
|
57
|
+
"@suss/sql": "0.30.0",
|
|
58
|
+
"@suss/values": "0.30.0",
|
|
59
59
|
"web-tree-sitter": "^0.26.11"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|