Skip to content

Commit

Permalink
(feature): introduce feature flag to simplify imports in python and r…
Browse files Browse the repository at this point in the history
…emove the nested `resources` directory (#3029)

* feature flag to remove resources directory

* (feature): support improved_imports

* add changelog and bump version

* fix imports

* fix typecheck

* fix formatting
  • Loading branch information
dsinghvi authored Feb 21, 2024
1 parent e3722bd commit 9c2dd78
Show file tree
Hide file tree
Showing 89 changed files with 4,981 additions and 16 deletions.
23 changes: 23 additions & 0 deletions generators/python/sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.11.2] - 2024-02-21
- Improvement (Beta): The Python generator now supports a configuration option called `improved_imports`. To enable
this configuration, just add the following to your generators.yml

```yaml
generators:
- name: fernapi/fern-python-sdk
...
config:
improved_imports: true
```
Enabling improved imports will remove the verbose `resources` directory in the SDK and make the imports
shorter. This will also improve the imports from Pylance and Pyright that are automaticaly generated

```python
# Before
from sdk.resources.fhir import Paient
# After
from sdk.fhir import Patient
```

## [0.11.1] - 2024-02-20

- Improvement: Python now supports specifying files to auto-export from the root `__init__.py` file, this means you can export custom classes and functions from your package for users to access like so:
Expand Down
2 changes: 1 addition & 1 deletion generators/python/sdk/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.11.0
0.11.2
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def run(
snippet_registry = SnippetRegistry()
snippet_writer = build_snippet_writer(
context=context.pydantic_generator_context,
improved_imports=False,
)

PydanticModelGenerator().generate_types(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def run(
snippet_registry = SnippetRegistry()
snippet_writer = self._build_snippet_writer(
context=context,
improved_imports=False,
)
self.generate_types(
generator_exec_wrapper=generator_exec_wrapper,
Expand Down Expand Up @@ -139,14 +140,15 @@ def is_flat_layout(
) -> bool:
return False

def _build_snippet_writer(self, context: PydanticGeneratorContext) -> SnippetWriter:
def _build_snippet_writer(self, context: PydanticGeneratorContext, improved_imports: bool = False) -> SnippetWriter:
"""
Note that this function is a copy of the function with the same name in
the fern_python.utils package. This is redeclared here to prevent an import
cycle.
"""
snippet_writer = SnippetWriter(
context=context,
improved_imports=improved_imports,
)

type_declaration_snippet_generator = TypeDeclarationSnippetGenerator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(
self.generator_config = generator_config
self.pydantic_generator_context = PydanticGeneratorContextImpl(
ir=ir,
type_declaration_referencer=TypeDeclarationReferencer(),
type_declaration_referencer=TypeDeclarationReferencer(skip_resources_module=custom_config.improved_imports),
generator_config=generator_config,
project_module_path=project_module_path,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,22 @@ def __init__(
client_class_name = custom_config.client_class_name or (
pascal_case(generator_config.organization) + pascal_case(generator_config.workspace_name)
)
self._error_declaration_referencer = ErrorDeclarationReferencer()
self._error_declaration_referencer = ErrorDeclarationReferencer(
skip_resources_module=custom_config.improved_imports
)
self._environments_enum_declaration_referencer = EnvironmentsEnumDeclarationReferencer(
client_class_name=client_class_name,
client_class_name=client_class_name, skip_resources_module=custom_config.improved_imports
)
self._subpackage_client_declaration_referencer = SubpackageClientDeclarationReferencer(
skip_resources_module=custom_config.improved_imports
)
self._subpackage_async_client_declaration_referencer = SubpackageAsyncClientDeclarationReferencer(
skip_resources_module=custom_config.improved_imports
)
self._subpackage_client_declaration_referencer = SubpackageClientDeclarationReferencer()
self._subpackage_async_client_declaration_referencer = SubpackageAsyncClientDeclarationReferencer()
self._root_client_declaration_referencer = RootClientDeclarationReferencer(
root_class_name=client_class_name,
root_client_filename=custom_config.client_filename,
skip_resources_module=custom_config.improved_imports,
)
self._custom_config = custom_config
self._project_module_path = project_module_path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class SDKCustomConfig(pydantic.BaseModel):
flat_layout: bool = False
pydantic_config: SdkPydanticModelCustomConfig = SdkPydanticModelCustomConfig()
additional_init_exports: Optional[List[ModuleExport]] = None
# Feature flag that improves imports in the
# Python SDK by removing nested `resources` directoy
improved_imports: bool = False

class Config:
extra = pydantic.Extra.forbid
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


class EnvironmentsEnumDeclarationReferencer(SdkDeclarationReferencer[None]):
def __init__(self, client_class_name: str):
super().__init__()
def __init__(self, client_class_name: str, skip_resources_module: bool):
super().__init__(skip_resources_module=skip_resources_module)
self._client_class_name = client_class_name

def get_filepath(self, *, name: None) -> Filepath:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


class RootClientDeclarationReferencer(SdkDeclarationReferencer[None]):
def __init__(self, root_class_name: str, root_client_filename: str):
super().__init__()
def __init__(self, root_class_name: str, root_client_filename: str, skip_resources_module: bool):
super().__init__(skip_resources_module=skip_resources_module)
self._root_class_name = root_class_name
self._root_client_filename = root_client_filename

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@


class SdkDeclarationReferencer(AbstractDeclarationReferencer[T], Generic[T]):
def __init__(self, *, skip_resources_module: bool) -> None:
self.skip_resources_module = skip_resources_module

def _get_directories_for_fern_filepath_part(
self,
*,
fern_filepath_part: ir_types.Name,
export_strategy: ExportStrategy,
) -> Tuple[Filepath.DirectoryFilepathPart, ...]:
if self.skip_resources_module:
return (
Filepath.DirectoryFilepathPart(
module_name=fern_filepath_part.snake_case.unsafe_name,
export_strategy=export_strategy,
),
)
return (
Filepath.DirectoryFilepathPart(
module_name="resources",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def run(
snippet_registry = SnippetRegistry()
snippet_writer = build_snippet_writer(
context=context.pydantic_generator_context,
improved_imports=custom_config.improved_imports,
)
PydanticModelGenerator().generate_types(
generator_exec_wrapper=generator_exec_wrapper,
Expand Down
6 changes: 4 additions & 2 deletions generators/python/src/fern_python/snippet/snippet_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ def __init__(
self,
context: PydanticGeneratorContext,
type_declaration_snippet_generator: Optional[TypeDeclarationSnippetGenerator] = None,
improved_imports: bool = False,
):
self._context = context
self._type_declaration_snippet_generator = type_declaration_snippet_generator
self._improved_imports = improved_imports

def get_snippet_for_example_type_shape(
self,
Expand Down Expand Up @@ -52,8 +54,8 @@ def get_module_path_for_declared_type_name(
name: ir_types.DeclaredTypeName,
) -> AST.ModulePath:
module_path = tuple([directory.snake_case.unsafe_name for directory in name.fern_filepath.package_path])
if len(module_path) > 0:
# If the type is defined in a subpackge, it needs to be imported with the 'resources'
if len(module_path) > 0 and not self._improved_imports:
# If the type is defined in a subpackage, it needs to be imported with the 'resources'
# intermediary key. Otherwise the types can be imported from the root package.
module_path = ("resources",) + module_path
return self._context.get_module_path_in_project(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
from fern_python.snippet import SnippetWriter, TypeDeclarationSnippetGenerator


def build_snippet_writer(context: PydanticGeneratorContext) -> SnippetWriter:
def build_snippet_writer(*, context: PydanticGeneratorContext, improved_imports: bool = False) -> SnippetWriter:
"""
Builds a new SnippetWriter. Using this function is preferred over
the SnippetWriter constructor due to the two-phase construction
process required between the SnippetWriter and TypeDeclarationSnippetGenerator.
"""
snippet_writer = SnippetWriter(
context=context,
improved_imports=improved_imports,
)

type_declaration_snippet_generator = TypeDeclarationSnippetGenerator(
Expand Down
8 changes: 7 additions & 1 deletion packages/seed/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ function addTestCommand(cli: Argv) {
demandOption: false,
description: "Runs on all fixtures if not provided"
})
.option("outputFolder", {
type: "string",
demandOption: false,
description: "A specific output folder to test against"
})
.option("keepDocker", {
type: "boolean",
demandOption: false,
Expand Down Expand Up @@ -121,7 +126,8 @@ function addTestCommand(cli: Argv) {
numDockers: argv.parallel,
taskContextFactory,
keepDocker: argv.keepDocker,
skipScripts: argv.skipScripts
skipScripts: argv.skipScripts,
outputFolder: argv.outputFolder
});
failurePresent = failurePresent || !passed;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/seed/src/commands/test/testWorkspaceFixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export async function testWorkspaceFixtures({
taskContextFactory,
numDockers,
keepDocker,
skipScripts
skipScripts,
outputFolder
}: {
workspace: SeedWorkspace;
irVersion: string | undefined;
Expand All @@ -71,6 +72,7 @@ export async function testWorkspaceFixtures({
numDockers: number;
keepDocker: boolean | undefined;
skipScripts: boolean;
outputFolder: string | undefined;
}): Promise<boolean> {
const lock = new Semaphore(numDockers);

Expand Down Expand Up @@ -102,6 +104,9 @@ export async function testWorkspaceFixtures({
);
if (fixtureConfig != null) {
for (const fixtureConfigInstance of fixtureConfig) {
if (outputFolder != null && fixtureConfigInstance.outputFolder !== outputFolder) {
continue;
}
testCases.push(
acquireLocksAndRunTest({
id: `${fixture}:${fixtureConfigInstance.outputFolder}`,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions seed/python-sdk/exhaustive/improved_imports/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Loading

0 comments on commit 9c2dd78

Please sign in to comment.