Skip to content
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

RSDK-9445: move generated python model out of main #4776

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
11 changes: 6 additions & 5 deletions cli/module_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ func populateAdditionalInfo(newModule *modulegen.ModuleInputs) {

titleCaser := cases.Title(language.Und)
replacer := strings.NewReplacer("_", " ", "-", " ")
snakeReplacer := strings.NewReplacer("-", "_", " ", "_")
spaceReplacer := modulegen.SpaceReplacer
newModule.ModulePascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ModuleName)))
newModule.ModuleCamel = strings.ToLower(string(newModule.ModulePascal[0])) + newModule.ModulePascal[1:]
Expand All @@ -382,7 +383,7 @@ func populateAdditionalInfo(newModule *modulegen.ModuleInputs) {
newModule.ResourceTypePascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ResourceType)))
newModule.ModelPascal = spaceReplacer.Replace(titleCaser.String(replacer.Replace(newModule.ModelName)))
newModule.ModelCamel = strings.ToLower(string(newModule.ModelPascal[0])) + newModule.ModelPascal[1:]
newModule.ModelLowercase = strings.ToLower(newModule.ModelPascal)
newModule.ModelSnake = snakeReplacer.Replace(newModule.ModelName)

modelTriple := fmt.Sprintf("%s:%s:%s", newModule.Namespace, newModule.ModuleName, newModule.ModelName)
newModule.ModelTriple = modelTriple
Expand Down Expand Up @@ -689,14 +690,14 @@ func generatePythonStubs(module modulegen.ModuleInputs) error {
return errors.Wrap(err, "cannot generate python stubs -- generator script encountered an error")
}

mainPath := filepath.Join(module.ModuleName, "src", "main.py")
resourcePath := filepath.Join(module.ModuleName, "src", "models", fmt.Sprintf("%s.py", module.ModelSnake))
//nolint:gosec
mainFile, err := os.Create(mainPath)
resourceFile, err := os.Create(resourcePath)
if err != nil {
return errors.Wrap(err, "cannot generate python stubs -- unable to open file")
}
defer utils.UncheckedErrorFunc(mainFile.Close)
_, err = mainFile.Write(out)
defer utils.UncheckedErrorFunc(resourceFile.Close)
_, err = resourceFile.Write(out)
if err != nil {
return errors.Wrap(err, "cannot generate python stubs -- unable to write to file")
}
Expand Down
47 changes: 47 additions & 0 deletions cli/module_generate/_templates/python/src/models/tmpl-resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import ClassVar, Mapping, Sequence, Self
from viam.proto.app.robot import ComponentConfig
from viam.proto.common import ResourceName
from viam.resource.base import ResourceBase
from viam.resource.easy_resource import EasyResource
from viam.resource.types import Model, ModelFamily
from viam.{{ .ResourceType }}s.{{ .ResourceSubtype }} import {{ .ResourceSubtypePascal }}


class {{ .ModelPascal }}({{ .ResourceSubtypePascal }}, EasyResource):
MODEL: ClassVar[Model] = "{{ .ModelTriple }}"

@classmethod
def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
"""This method creates a new instance of this {{ .ResourceSubtype }}.
The default implementation sets the name from the `config` parameter and then calls `reconfigure`.

Args:
config (ComponentConfig): The configuration for this resource
dependencies (Mapping[ResourceName, ResourceBase]): The dependencies (both implicit and explicit)

Returns:
Self: The resource
"""
return super().new(config, dependencies)

@classmethod
def validate_config(cls, config: ComponentConfig) -> Sequence[str]:
"""This method allows you to validate the configuration object received from the machine,
as well as to return any implicit dependencies based on that `config`.

Args:
config (ComponentConfig): The configuration for this resource

Returns:
Sequence[str]: A list of implicit dependencies
"""
return []

def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]):
"""This method allows you to dynamically update your service when it receives a new `config` object.

Args:
config (ComponentConfig): The new configuration
dependencies (Mapping[ResourceName, ResourceBase]): Any dependencies (both implicit and explicit)
"""
return super().reconfigure(config, dependencies)
48 changes: 1 addition & 47 deletions cli/module_generate/_templates/python/src/tmpl-main.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,6 @@
import asyncio
from typing import ClassVar, Mapping, Sequence, Self
from viam.module.module import Module
from viam.proto.app.robot import ComponentConfig
from viam.proto.common import ResourceName
from viam.resource.base import ResourceBase
from viam.resource.easy_resource import EasyResource
from viam.resource.types import Model, ModelFamily
from viam.{{ .ResourceType }}s.{{ .ResourceSubtype }} import {{ .ResourceSubtypePascal }}


class {{ .ModelPascal }}({{ .ResourceSubtypePascal }}, EasyResource):
MODEL: ClassVar[Model] = "{{ .ModelTriple }}"

@classmethod
def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:
"""This method creates a new instance of this {{ .ResourceSubtype }}.
The default implementation sets the name from the `config` parameter and then calls `reconfigure`.

Args:
config (ComponentConfig): The configuration for this resource
dependencies (Mapping[ResourceName, ResourceBase]): The dependencies (both implicit and explicit)

Returns:
Self: The resource
"""
return super().new(config, dependencies)

@classmethod
def validate_config(cls, config: ComponentConfig) -> Sequence[str]:
"""This method allows you to validate the configuration object received from the machine,
as well as to return any implicit dependencies based on that `config`.

Args:
config (ComponentConfig): The configuration for this resource

Returns:
Sequence[str]: A list of implicit dependencies
"""
return []

def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]):
"""This method allows you to dynamically update your service when it receives a new `config` object.

Args:
config (ComponentConfig): The new configuration
dependencies (Mapping[ResourceName, ResourceBase]): Any dependencies (both implicit and explicit)
"""
return super().reconfigure(config, dependencies)
from .models.{{ .ModelSnake }} import {{ .ModelPascal }}


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion cli/module_generate/modulegen/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ type ModuleInputs struct {
ResourceTypePascal string `json:"-"`
ModelPascal string `json:"-"`
ModelCamel string `json:"-"`
ModelSnake string `json:"-"`
ModelTriple string `json:"-"`
ModelLowercase string `json:"-"`
ModelReadmeLink string `json:"-"`
SDKVersion string `json:"-"`
}
Expand Down
22 changes: 8 additions & 14 deletions cli/module_generate/scripts/generate_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,15 @@ def main(
imports.append("from typing import Optional")
imports.append("from viam.utils import ValueTypes")
elif cstmt.name == "get_geometries":
imports.append("from typing import List, Optional")
imports.append("from typing import Any, Dict, List, Optional")
imports.append("from viam.proto.common import Geometry")

model_name_pascal = "".join(
[word.capitalize() for word in slugify(model_name).split("-")]
)
main_file = '''
import asyncio
resource_file = '''
from typing import ClassVar, Mapping, Sequence
from typing_extensions import Self
from viam.module.module import Module
from viam.proto.app.robot import ComponentConfig
from viam.proto.common import ResourceName
from viam.resource.base import ResourceBase
Expand Down Expand Up @@ -232,11 +230,6 @@ def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceNam

{8}
{9}


if __name__ == '__main__':
asyncio.run(Module.run_from_registry())

'''.format(
"\n".join(list(set(imports))),
resource_type,
Expand All @@ -249,19 +242,20 @@ def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceNam
'\n\n'.join([subclass for subclass in subclasses]),
'\n\n'.join([f'{method}' for method in abstract_methods]),
)
f_name = os.path.join(mod_name, "src", "main.py")
f_name = os.path.join(mod_name, "src", "models", "resource.py")
with open(f_name, "w+") as f:
f.write(main_file)
f.write(resource_file)
try:
f.seek(0)
subprocess.check_call([sys.executable, "-m", "black", f_name, "-q"])
f.seek(0)
main_file = f.read()
resource_file = f.read()
except subprocess.CalledProcessError:
pass
os.remove(f_name)
sorted_main = isort.code(main_file)
return sorted_main
sorted_code = isort.code(resource_file)

return sorted_code


if __name__ == "__main__":
Expand Down
Loading