-
Notifications
You must be signed in to change notification settings - Fork 1
All the emitter framework changes I made to get the COI demo working #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/model-interface
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| import { abcModule, dataclassesModule } from "#python/builtins.js"; | ||
| import { abcModule, dataclassesModule, typingModule } from "#python/builtins.js"; | ||
| import { type Children, For, List, mapJoin, Show } from "@alloy-js/core"; | ||
| import * as py from "@alloy-js/python"; | ||
| import { type Interface, type Model, type ModelProperty, type Operation } from "@typespec/compiler"; | ||
| import type { TemplateDeclarationNode } from "@typespec/compiler/ast"; | ||
| import type { Typekit } from "@typespec/compiler/typekit"; | ||
| import { createRekeyableMap } from "@typespec/compiler/utils"; | ||
| import { useTsp } from "../../../core/context/tsp-context.js"; | ||
|
|
@@ -152,16 +153,25 @@ function getExtendsType($: Typekit, type: Model | Interface): Children | undefin | |
| * @param abstract - Whether the class is abstract. | ||
| * @returns The bases type for the class declaration. | ||
| */ | ||
| function createBasesType($: Typekit, props: ClassDeclarationProps, abstract: boolean) { | ||
| const globalBasesType = isTypedClassDeclarationProps(props) | ||
| ? getExtendsType($, props.type) | ||
| : undefined; | ||
| let basesType = props.bases ? props.bases : (globalBasesType ?? undefined); | ||
| function createBasesType( | ||
| $: Typekit, | ||
| props: ClassDeclarationProps, | ||
| abstract: boolean, | ||
| extraBases: Children[] = [], | ||
| ) { | ||
| if (isTypedClassDeclarationProps(props)) { | ||
| const extend = getExtendsType($, props.type); | ||
| if (extend) { | ||
| extraBases.push(extend); | ||
| } | ||
| } | ||
| const allBases = (props.bases ? props.bases : []).concat(extraBases); | ||
| const basesType = allBases.length > 0 ? allBases : undefined; | ||
| if (!abstract) return basesType; | ||
|
|
||
| const abcBase = abcModule["."]["ABC"]; | ||
| if (Array.isArray(basesType)) return [abcBase, ...basesType]; | ||
| if (basesType != null) return [abcBase, basesType]; | ||
| if (Array.isArray(basesType)) return [...basesType, abcBase]; | ||
| if (basesType != null) return [basesType, abcBase]; | ||
| return [abcBase]; | ||
| } | ||
|
|
||
|
|
@@ -174,10 +184,48 @@ export function ClassDeclaration(props: ClassDeclarationProps) { | |
| const { $ } = useTsp(); | ||
|
|
||
| // If we are explicitly overriding the class as abstract or the type is not a model, we need to create an abstract class | ||
| let abstract = | ||
| const abstract = | ||
| ("abstract" in props && props.abstract) || ("type" in props && !$.model.is(props.type)); | ||
| let docElement = createDocElement($, props); | ||
| let basesType = createBasesType($, props, abstract); | ||
| const docElement = createDocElement($, props); | ||
|
|
||
| const extraBases = []; | ||
| let typeVars = null; | ||
| const typeArgs = []; | ||
| if (isTypedClassDeclarationProps(props)) { | ||
| if ( | ||
| !props.type.isFinished && | ||
| (props.type.node as TemplateDeclarationNode)?.templateParameters | ||
| ) { | ||
|
Comment on lines
+195
to
+198
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mauriciogardini this is what I was referring to here #45 (comment) |
||
| const templateParameters = (props.type.node as TemplateDeclarationNode)?.templateParameters; | ||
| typeVars = ( | ||
| <> | ||
| <For each={templateParameters} hardline> | ||
| {(node) => { | ||
| const typeVar = ( | ||
| <py.FunctionCallExpression | ||
| target={typingModule["."].TypeVar} | ||
| args={[<py.Atom jsValue={node.id.sv} />]} | ||
| /> | ||
| ); | ||
| return <py.VariableDeclaration name={node.id.sv} initializer={typeVar} />; | ||
|
|
||
| // ("${node.id.sv}")`; | ||
| // return <py.Declaration name={String(node.id.sv)}></py.Declaration>; | ||
| }} | ||
| </For> | ||
| </> | ||
| ); | ||
| for (const templateParamter of templateParameters) { | ||
| typeArgs.push(templateParamter.id.sv); | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't great, we're just making a list of strings. These should probably be references but I didn't have time to figure out how to make that work. |
||
| } | ||
| } | ||
|
Comment on lines
+195
to
+221
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is all aimed at determining if there are template parameters that are unfulfilled and, if so, adding them as type arguments to a |
||
|
|
||
| if (typeArgs.length > 0) { | ||
| extraBases.push(<py.TypeReference refkey={typingModule["."].Generic} typeArgs={typeArgs} />); | ||
| } | ||
| } | ||
|
|
||
| const basesType = createBasesType($, props, abstract, extraBases); | ||
|
|
||
| if (!isTypedClassDeclarationProps(props)) { | ||
| return ( | ||
|
|
@@ -199,12 +247,10 @@ export function ClassDeclaration(props: ClassDeclarationProps) { | |
|
|
||
| const refkeys = declarationRefkeys(props.refkey, props.type); | ||
| let dataclass: any = null; | ||
| if (!abstract) { | ||
| // Array-based models should be rendered as normal classes, not dataclasses (e.g., model Foo is Array<T>) | ||
| const isArrayModel = $.model.is(props.type) && $.array.is(props.type); | ||
| if (!isArrayModel) { | ||
| dataclass = dataclassesModule["."]["dataclass"]; | ||
| } | ||
| // Array-based models should be rendered as normal classes, not dataclasses (e.g., model Foo is Array<T>) | ||
| const isArrayModel = $.model.is(props.type) && $.array.is(props.type); | ||
| if (!isArrayModel) { | ||
| dataclass = dataclassesModule["."]["dataclass"]; | ||
| } | ||
| const classBody = createClassBody($, props, abstract); | ||
|
|
||
|
|
@@ -218,11 +264,13 @@ export function ClassDeclaration(props: ClassDeclarationProps) { | |
|
|
||
| return ( | ||
| <> | ||
| <Show when={dataclass}> | ||
| @{dataclass} | ||
| <Show when={!!typeVars}> | ||
| {typeVars} | ||
| <hbr /> | ||
| <line /> | ||
| </Show> | ||
|
Comment on lines
+267
to
271
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Even then, I'm not sure there is a defined style for where TypeVars should go. |
||
| <Show when={abstract}> | ||
| <Show when={dataclass}> | ||
| @{dataclass}(kw_only=True) | ||
| <hbr /> | ||
| </Show> | ||
| <MethodProvider value={props.methodType}> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from "./atom/atom.jsx"; | ||
| export * from "./class-declaration/class-declaration.jsx"; | ||
| export * from "./enum-declaration/enum-declaration.jsx"; | ||
| export * from "./function-declaration/function-declaration.jsx"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,17 @@ | ||
| import { Experimental_OverridableComponent } from "#core/components/index.js"; | ||
| import { useTsp } from "#core/context/index.js"; | ||
| import { reportPythonDiagnostic } from "#python/lib.js"; | ||
| import { For } from "@alloy-js/core"; | ||
| import { code, For } from "@alloy-js/core"; | ||
| import * as py from "@alloy-js/python"; | ||
| import type { IntrinsicType, Model, Scalar, Type } from "@typespec/compiler"; | ||
| import { | ||
| isNeverType, | ||
| type IntrinsicType, | ||
| type Model, | ||
| type Scalar, | ||
| type TemplatedTypeBase, | ||
| type Type, | ||
| } from "@typespec/compiler"; | ||
| import type { TemplateParameterDeclarationNode } from "@typespec/compiler/ast"; | ||
| import type { Typekit } from "@typespec/compiler/typekit"; | ||
| import { datetimeModule, decimalModule, typingModule } from "../../builtins.js"; | ||
| import { efRefkey } from "../../utils/refkey.js"; | ||
|
|
@@ -36,6 +44,9 @@ export function TypeExpression(props: TypeExpressionProps) { | |
| switch (type.kind) { | ||
| case "Scalar": // Custom types based on primitives (Intrinsics) | ||
| case "Intrinsic": // Language primitives like `string`, `number`, etc. | ||
| if (isNeverType(type)) { | ||
| return typingModule["."]["Never"]; | ||
| } | ||
| return <>{getScalarIntrinsicExpression($, type)}</>; | ||
| case "Boolean": | ||
| case "Number": | ||
|
|
@@ -64,8 +75,13 @@ export function TypeExpression(props: TypeExpressionProps) { | |
| return <RecordExpression elementType={elementType} />; | ||
| } | ||
|
|
||
| if (isTemplateVar(type)) { | ||
| return <TypeExpression type={type.templateMapper?.args[0] as Type} />; | ||
| } | ||
|
Comment on lines
+78
to
+80
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The weird thing is that models used as template arguments are treated as models here. |
||
| reportPythonDiagnostic($.program, { code: "python-unsupported-type", target: type }); | ||
| break; | ||
| case "TemplateParameter": | ||
| return code`${String((type.node as TemplateParameterDeclarationNode).id.sv)}`; | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably can do a lot better than a string! Should be a reference. |
||
|
|
||
| // TODO: Models will be implemented separately | ||
| // return <InterfaceExpression type={type} />; | ||
|
|
@@ -157,7 +173,12 @@ function getScalarIntrinsicExpression($: Typekit, type: Scalar | IntrinsicType): | |
| return pythonType; | ||
| } | ||
|
|
||
| function isTemplateVar(type: Type): boolean { | ||
| return (type as TemplatedTypeBase).templateMapper !== undefined; | ||
| } | ||
|
|
||
| function isDeclaration($: Typekit, type: Type): boolean { | ||
| if (isTemplateVar(type)) return false; | ||
| switch (type.kind) { | ||
| case "Namespace": | ||
| case "Interface": | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| export * from "./builtins.js"; | ||
| export * from "./components/index.js"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # MacOS | ||
| .DS_Store | ||
|
|
||
| # Default TypeSpec output | ||
| tsp-output/ | ||
| dist/ | ||
|
|
||
| # Dependency directories | ||
| node_modules/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // @ts-check | ||
| import eslint from "@eslint/js"; | ||
| import tsEslint from "typescript-eslint"; | ||
|
|
||
| export default tsEslint.config( | ||
| { | ||
| ignores: ["**/dist/**/*", "**/.temp/**/*"], | ||
| }, | ||
| eslint.configs.recommended, | ||
| ...tsEslint.configs.recommended, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| { | ||
| "name": "python", | ||
| "version": "0.1.0", | ||
| "type": "module", | ||
| "main": "dist/src/index.js", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/src/index.d.ts", | ||
| "default": "./dist/src/index.js" | ||
| }, | ||
| "./testing": { | ||
| "types": "./dist/src/testing/index.d.ts", | ||
| "default": "./dist/src/testing/index.js" | ||
| } | ||
| }, | ||
| "peerDependencies": { | ||
| "@typespec/compiler": "workspace:^" | ||
| }, | ||
| "dependencies": { | ||
| "@alloy-js/core": "^0.20.0", | ||
| "@alloy-js/python": "link:/Users/srice/code/alloy/packages/python", | ||
| "@typespec/emitter-framework": "workspace:^", | ||
| "prettier": "~3.6.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@alloy-js/cli": "^0.20.0", | ||
| "@types/node": "latest", | ||
| "@typescript-eslint/eslint-plugin": "^8.15.0", | ||
| "@typescript-eslint/parser": "^8.15.0", | ||
| "@typespec/compiler": "workspace:^", | ||
| "eslint": "^9.15.0", | ||
| "prettier": "^3.3.3", | ||
| "typescript": "^5.3.3" | ||
| }, | ||
| "scripts": { | ||
| "build": "alloy build", | ||
| "test": "node --test 'dist/test**/*.test.js'", | ||
| "lint": "eslint src/ test/ --report-unused-disable-directives --max-warnings=0", | ||
| "lint:fix": "eslint . --report-unused-disable-directives --fix", | ||
| "format": "prettier . --write", | ||
| "format:check": "prettier --check ." | ||
| }, | ||
| "private": true | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| trailingComma: "all" | ||
| printWidth: 120 | ||
| quoteProps: "consistent" | ||
| endOfLine: lf | ||
| arrowParens: always | ||
| plugins: | ||
| - "./node_modules/@typespec/prettier-plugin-typespec/dist/index.js" | ||
| overrides: [{ "files": "*.tsp", "options": { "parser": "typespec" } }] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes in this function are aimed at allowing us to specify additional bases for the purpose of adding
Generic.Those bases are generated in
ClassDeclaration.