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
bug: cyclic dependency error in Python distribution of projen #811
Comments
Actual synthesis with `.projenrc.py` is currently not functional because of #811, but this commit adds the CLI flag along with projenrc.py file generation so the feature can be experimentally tested going forward. Since it's still not functional, I did not update the docs to mention `.projenrc.py` since it would probably cause more confusion than help. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Having looked into the generated code a bit I believe that is the issue is src/project.ts (which translates to The (perhaps unfavorable) way to fix this immediate issue would be to move |
We need to restructure the files into submodules that don't take any cyclic dependencies. I was wondering if there is some eslint rule that can help with that. |
This issue is now marked as stale because it hasn't seen activity for a while. Add a comment or it will be closed soon. |
Keep open |
This issue is now marked as stale because it hasn't seen activity for a while. Add a comment or it will be closed soon. |
Keep |
This issue is now marked as stale because it hasn't seen activity for a while. Add a comment or it will be closed soon. |
@eladb Perhaps the eslint import/no-cycle rule could be used? It would actually report more errors than we care about since it would report cycles between files in the same jsii submodule - but it's still a good starting point. I was revisiting this issue briefly, just focusing on the dependency cycles including
Option 1) seems weird at first, but I wonder if it might be the most pragmatic. We could keep source code files in subdirectories for organizational purposes, but export them not as named submodules. I'm not familiar enough with jsii submodules to understand what the drawbacks of this would be, besides having to worry a little bit about naming collisions. Option 2) could be achieved by removing all public fields/methods on Option 3) I'm not how this could be achieved. I thought of having components depend on an Curious what you think about these options? |
There are two aspects here: (1) how do we enforce that submodules do not have cyclic dependencies; and (2) how do we untangle the mess we have in projen today. For (1) I think we need a robust long-term solution to this problem, and I think the solution should be in the JSII compiler. One of the tenets of JSII is that the compiler should not allow you to write code that will not be translatable to other languages. This is a good example of a missing check in the JSII compiler. Let's explore with @RomainMuller what it would take to add such a check (likely under a feature flag at this point given its a breaking change). For (2) I think you are on the right direction - basically this requires a redesign, especially of the low-level types ( Here's a wild idea: create a new library called WDYT? |
Agree with (1), this should be compile-time checked in JSII. For (2) I'm OK with moving pieces into a separate library, though it's not immediately clear to me how that makes the de-spagettizing easier. (There are could be other reasons to split the code into multiple repos, but I think it also has some costs - e.g. all else equal, it's a better contributor experience when the code is all in a single repo).
Cool - "anything referenced by |
I guess you are right that a separate library is just a technique to enforce the layering and we should have an in-module way of doing it. If we can get the linter to help us enforce it in-module, then let's start collecting all of the core stuff under a I am wondering if maybe this is our version of mono-repos. Can we do something like have a |
Fixes #811 Resolves the cyclic dependency errors by reorganizing the submodules within projen so that there are no more import cycles (between submodules). This requires changing the location of MANY classes and associated types. This enables us to add support in later PRs for writing projenrc files in Python and Go. In TypeScript projects, classes and types should be imported in one of two ways: ```ts // method 1 import { awscdk } from "projen"; const project = new awscdk.AwsCdkConstructLibrary({ ... }); // method 2 import { AwsCdkConstructLibrary } from "projen/awscdk"; const project = new AwsCdkConstructLibrary({ ... }); ``` Due to these changes, running `projen new --from <module>` will not work if the module was compiled with a version of projen before 0.37.x AND you are using a version of projen CLI that is 0.37.x or later. It is still possible to create these projects if you use an older version of projen: `npx projen@0.36.5 --from <module>`. ---- I've had to comment out several tests involving external modules due to these breaking changes. I did look for a while to see if there's some way to try redirecting some of these project types so you could still import older project types, but I could not see a way to do this. If a new projenrc file instantiates a `MyCustomProject` where the original library says `MyCustomProject` extends `projen.TypeScriptProject`, I can't see any way to work around this if we are using the newest projen version. (I could update the integration tests to pass e.g. `--projen-version=0.36.5`, but that would defeat the tests' purpose I think?) To prevent these kinds of dependency cycles from reoccurring, I've added an integration test workflow that checks it is possible to synthesize a Python base project in python using the generated python package. This can be removed once JSII support has been added for submodule cycle checking (aws/jsii#2511). ---- BREAKING CHANGE: All project class types are now within submodules. `projen new --from` may fail if project is not compatible with the version of the CLI you are using. Full list of type changes: - All classes and types within the `git`, `json`, and `tasks` submodules can now be imported directly from the root. - The class `AwsCdkConstructLibrary` is now `awscdk.AwsCdkConstructLibrary` - The class `AwsCdkTypeScriptApp` is now `awscdk.AwsCdkTypeScriptApp` - The class `Cdk8sTypeScriptApp` is now `cdk8s.Cdk8sTypeScriptApp` - The class `ConstructLibrary` is now `cdk.ConstructLibrary` - The class `ConstructLibraryAws` is now `awscdk.ConstructLibraryAws` - The class `ConstructLibraryCdk8s` is now `cdk8s.ConstructLibraryCdk8s` - The class `ConstructLibraryCdktf` is now `cdktf.ConstructLibraryCdktf` - The class `Eslint` is now `javascript.Eslint` - The class `GitHubProject` is now `github.GitHubProject` - The class `Jest` is now `javascript.Jest` - The class `JsiiProject` is now `cdk.JsiiProject` - The class `NodePackage` is now `javascript.NodePackage` - The class `NodeProject` is now `javascript.NodeProject` - The class `TypeScriptAppProject` is now `typescript.TypeScriptAppProject` - The class `TypeScriptLibraryProject` is now `typescript.TypeScriptLibraryProject` - The class `TypeScriptProject` is now `typescript.TypeScriptProject` - The class `TypescriptConfig` is now `javascript.TypescriptConfig` (!) - The class `UpgradeDependencies` is now `javascript.UpgradeDependencies` - The class `UpgradeDependenciesSchedule` is now `javascript.UpgradeDependenciesSchedule` - The interface `AwsCdkConstructLibraryOptions` is now `awscdk.AwsCdkConstructLibraryOptions` - The interface `AwsCdkTypeScriptAppOptions` is now `awscdk.AwsCdkTypeScriptAppOptions` - The interface `Catalog` is now `cdk.Catalog` - The interface `Cdk8sTypeScriptAppOptions` is now `cdk8s.Cdk8sTypeScriptAppOptions` - The interface `CodeArtifactOptions` is now `javascript.CodeArtifactOptions` - The interface `ConstructLibraryAwsOptions` is now `awscdk.ConstructLibraryAwsOptions` - The interface `ConstructLibraryCdk8sOptions` is now `cdk8s.ConstructLibraryCdk8sOptions` - The interface `ConstructLibraryCdktfOptions` is now `cdktf.ConstructLibraryCdktfOptions` - The interface `ConstructLibraryOptions` is now `cdk.ConstructLibraryOptions` - The interface `CoverageThreshold` is now `javascript.CoverageThreshold` - The interface `EslintOptions` is now `javascript.EslintOptions` - The interface `EslintOverride` is now `javascript.EslintOverride` - The interface `GitHubProjectOptions` is now `github.GitHubProjectOptions` - The interface `HasteConfig` is now `javascript.HasteConfig` - The interface `JestConfigOptions` is now `javascript.JestConfigOptions` - The interface `JestOptions` is now `javascript.JestOptions` - The interface `JsiiDotNetTarget` is now `cdk.JsiiDotNetTarget` - The interface `JsiiGoTarget` is now `cdk.JsiiGoTarget` - The interface `JsiiJavaTarget` is now `cdk.JsiiJavaTarget` - The interface `JsiiProjectOptions` is now `cdk.JsiiProjectOptions` - The interface `JsiiPythonTarget` is now `cdk.JsiiPythonTarget` - The interface `NodePackageOptions` is now `javascript.NodePackageOptions` - The interface `NodeProjectOptions` is now `javascript.NodeProjectOptions` - The interface `NodeWorkflowSteps` is now `javascript.NodeWorkflowSteps` - The interface `PeerDependencyOptions` is now `javascript.PeerDependencyOptions` - The interface `TypeScriptCompilerOptions` is now `javascript.TypeScriptCompilerOptions` (!) - The interface `TypeScriptLibraryProjectOptions` is now `typescript.TypeScriptLibraryProjectOptions` - The interface `TypeScriptProjectOptions` is now `typescript.TypeScriptProjectOptions` - The interface `TypescriptConfigOptions` is now `javascript.TypescriptConfigOptions` (!) - The interface `UpgradeDependenciesOptions` is now `javascript.UpgradeDependenciesOptions` - The interface `UpgradeDependenciesWorkflowOptions` is now `javascript.UpgradeDependenciesWorkflowOptions` - The enum `NpmAccess` is now `javascript.NpmAccess` - The enum `Stability` is now `cdk.Stability` - The enum `TypeScriptJsxMode` is now `javascript.TypeScriptJsxMode` - The enum `TypeScriptModuleResolution` is now `javascript.TypeScriptModuleResolution` --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Since projen is built with jsii, it is also vended as a python library on pypi (see here).
However, it seems that using this module to synthesize even simple projects isn't possible due to cyclic dependencies. For example, if we install
projen
with pip and write a project definition in.projenrc.py
like this:Then running
python .projenrc.py
throws the following error:My best guess of the cause of this is that
Project
directly referencesComponent
in several of its methods, and likewiseProject
referencesComponent
in its constructor.Is it possible we could resolve this cycle by changing one of these to an interface like
IProject
orIComponent
instead?I'm also curious how other JSII libraries like the AWS CDK go about avoiding (or resolving) cyclic dependencies like this.
Note: since this is just the first error that's thrown, it's not clear fixing this alone would be enough to get .projenrc.py working in full. When I ran
madge --circular --extensions ts src
it reports there are upwards of 45 potential circular dependencies as of posting (see below) -- though it's possible not all of these are relevant to synthesizing Python projects.The text was updated successfully, but these errors were encountered: