Skip to content

Commit

Permalink
Merge pull request #51 from MyApaulogies/python-style
Browse files Browse the repository at this point in the history
Move Python documentation comments to docstring
  • Loading branch information
dinkelk authored Jun 27, 2024
2 parents 4a6c280 + b5063dd commit deeca6e
Show file tree
Hide file tree
Showing 148 changed files with 2,417 additions and 1,636 deletions.
10 changes: 6 additions & 4 deletions gen/generators/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def __init__(self):
type_format_dictionary = {"U": "%d", "I": "%d", "F": "%f"}


# Function which produces a format string to print an Ada type:
def create_type_print_strings(theAssembly):
"""Function which produces a format string to print an Ada type."""
for name, model in theAssembly.complex_types.items():
string = ""
model_type = type(model).__name__
Expand Down Expand Up @@ -165,9 +165,11 @@ def create_type_print_strings(theAssembly):
assert False, "Cannot handle models of type: " + str(model_type)


# Function which produces a type field string for all types
# in the assembly mode.
def create_type_field_strings(theAssembly):
"""
Function which produces a type field string for all types
in the assembly mode.
"""
def form_field_strings(top_model, model, prefix=""):
model_type = type(model).__name__
if model_type in ["record", "array"]:
Expand Down Expand Up @@ -250,8 +252,8 @@ def generate(self, input_filename):
a = v.apply(a)
print(a.render(self.template))

# Depend on any commands, data products, events models that this component uses:
def depends_on(self, input_filename):
"""Depend on any commands, data products, events models that this component uses."""
dirname, view_name, assembly_name, *ignore = self._split_input_filename(
input_filename
)
Expand Down
126 changes: 72 additions & 54 deletions gen/generators/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import os.path


# Special helper function which allows you to dynamically create a basic generator at
# runtime. You can specify the module that you would like the generator to be part of.
def create_basic_generator(
model_class,
template_name,
Expand All @@ -16,6 +14,10 @@ def create_basic_generator(
camel_case_filename=False,
ignore_cache=False
):
"""
Special helper function which allows you to dynamically create a basic generator at
runtime. You can specify the module that you would like the generator to be part of.
"""
#
# Dynamically generate a class of the form:
#
Expand Down Expand Up @@ -50,13 +52,14 @@ def init_func(self):
)


# Add a class to a specific module. By default, calling this will add the generator to
# the module you called the function from.
def add_class_to_module(cls, module=globals()):
"""
Add a class to a specific module. By default, calling this will add the generator to
the module you called the function from.
"""
module[cls.__name__] = cls


# Add a basic generator to a specific module:
def add_basic_generator_to_module(
model_class,
template,
Expand All @@ -66,6 +69,9 @@ def add_basic_generator_to_module(
camel_case_filename=False,
ignore_cache=False
):
"""
Add a basic generator to a specific module:
"""
module_name = module["__name__"]
cls = create_basic_generator(
model_class,
Expand All @@ -79,7 +85,6 @@ def add_basic_generator_to_module(
add_class_to_module(cls, module)


# Add a list of basic generators to specific module, where each generator has a different output template.
def add_basic_generators_to_module(
model_class,
templates,
Expand All @@ -89,6 +94,9 @@ def add_basic_generators_to_module(
camel_case_filename=False,
ignore_cache=False
):
"""
Add a list of basic generators to specific module, where each generator has a different output template.
"""
for template in templates:
add_basic_generator_to_module(
model_class,
Expand All @@ -101,42 +109,44 @@ def add_basic_generators_to_module(
)


#
# This package contains a basic generator which implements a generator pattern
# common for most Adamant generators. Generators should create a child class inheriting from
# both this class and generator_base. ie. something like this:
#
# class new_generator(basic_generator, generator_base):
# def __init__(self):
# basic_generator.__init__(self, model_class=new_model.new_model, template_filename="name_cool_file.ads")
#
# where model_class is the python class that contains the generator model and template_filename contains
# the filename of the template used to render the output file.
#
# Most generators will work as expected if the follow the Adamant generator pattern. Besides implementing
# the generator as shown above, the template file name should exactly mimic the format of the output
# filename with the string "name" substituted in for the model name that generates the output. For example
# if the model name is something like awesome.component.yaml then the following template names will
# result in the shown output names:
#
# name.html -> awesome.html
# whatever.ads -> whatever.ads
# component-name-implementation.adb -> component-awesome-implementation.adb
#
# Output files will be constructed relative to the input model file in their default adamant directories.
# The rules are outlined below:
#
# files extension ".tex" -> doc/build/tex
# files extension ".ad[s,b]" -> build/src
# files ending in "[main, test, -implementation, -implementation-tester].ad[s,b]" -> build/template
# all others -> build/<file extension>
#
# If this behavior doesn't fit your generator you have two choices:
#
# 1) Override the methods of basic_generator where you need different behavior
# 2) Write your generator from scratch inheriting from generator_base
#
class basic_generator(object):
"""
This package contains a basic generator which implements a generator pattern
common for most Adamant generators. Generators should create a child class inheriting from
both this class and generator_base. ie. something like this:
class new_generator(basic_generator, generator_base):
def __init__(self):
basic_generator.__init__(self, model_class=new_model.new_model, template_filename="name_cool_file.ads")
where model_class is the python class that contains the generator model and template_filename contains
the filename of the template used to render the output file.
Most generators will work as expected if the follow the Adamant generator pattern. Besides implementing
the generator as shown above, the template file name should exactly mimic the format of the output
filename with the string "name" substituted in for the model name that generates the output. For example
if the model name is something like awesome.component.yaml then the following template names will
result in the shown output names:
name.html -> awesome.html
whatever.ads -> whatever.ads
component-name-implementation.adb -> component-awesome-implementation.adb
Output files will be constructed relative to the input model file in their default adamant directories.
The rules are outlined below:
files extension ".tex" -> doc/build/tex
files extension ".ad[s,b]" -> build/src
files ending in "[main, test, -implementation, -implementation-tester].ad[s,b]" -> build/template
all others -> build/<file extension>
If this behavior doesn't fit your generator you have two choices:
1) Override the methods of basic_generator where you need different behavior
2) Write your generator from scratch inheriting from generator_base
"""
def __init__(
self,
model_class,
Expand Down Expand Up @@ -167,26 +177,30 @@ def __init__(
self.extension = ext[1:]
self.descriptor = name.split("name")[-1]

# Cache model object for speed:
def model_object(self, input_filename):
"""Cache model object for speed."""
if input_filename not in self._model_obj:
self._model_obj[input_filename] = self.model_cls(input_filename, ignore_cache=self.ignore_cache)
return self._model_obj[input_filename]

def input_file_regex(self):
return r".*\." + self.model_type + r"\.yaml$"

# Extracts useful info from the input filename. Examples:
#
# /path/to/my_component.component.yaml -> (/path/to, None, my_component, component, yaml)
# /path/to/my_test.my_component.tests.yaml -> (/path/to, my_test, my_component, tests, yaml)
#
def _split_input_filename(self, input_filename):
"""
Extracts useful info from the input filename. Examples:
/path/to/my_component.component.yaml -> (/path/to, None, my_component, component, yaml)
/path/to/my_test.my_component.tests.yaml -> (/path/to, my_test, my_component, tests, yaml)
"""
return redo_arg.split_model_filename(input_filename, self.model_type)

# Return the default build directory for this generator based on the
# template's file extension and name.
def _get_default_build_dir(self):
"""
Return the default build directory for this generator based on the
template's file extension and name.
"""
# Set defaults:
build_dir = "build"
sub_dir = self.extension
Expand All @@ -211,9 +225,11 @@ def _get_default_build_dir(self):

return build_dir + os.sep + sub_dir

# The default output filename is the name of the template with last instance of the string "name"
# replaced with the name of the specific model we are using as input file.
def _get_default_output_filename(self, model_name):
"""
The default output filename is the name of the template with last instance of the string "name"
replaced with the name of the specific model we are using as input file.
"""
a = self.template_basename.rsplit("name", maxsplit=1)
name = model_name.join(a)
if self.camel_case_filename:
Expand All @@ -223,9 +239,11 @@ def _get_default_output_filename(self, model_name):
name = ".".join(split_name)
return name

# Override this if it does not work for your specific generator. This function basically
# uses the basic generator defaults for output file location and name.
def output_filename(self, input_filename):
"""
Override this if it does not work for your specific generator. This function basically
uses the basic generator defaults for output file location and name.
"""
# Get info from input filename:
dirname, specific_name, model_name, *ignore = self._split_input_filename(
input_filename
Expand Down Expand Up @@ -277,8 +295,8 @@ def generate(self, input_filename, methods_to_call_on_model_obj=[]):
)
)

# Depend on the primary model dependencies:
def depends_on(self, input_filename):
"""Depend on the primary model dependencies."""
if self.has_dependencies:
m = self.model_object(input_filename)
if hasattr(m, "get_dependencies"):
Expand Down
2 changes: 1 addition & 1 deletion gen/generators/memory_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
)


# Base class for generating a memory/register map and running GNATprove on the result.
class map_ads_generator(basic_generator):
"""Base class for generating a memory/register map and running GNATprove on the result."""
def __init__(self, model_class, template_filename):
basic_generator.__init__(
self, model_class=model_class, template_filename=template_filename
Expand Down
6 changes: 4 additions & 2 deletions gen/generators/packed_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ def __init__(self):
)


# Special enum generator for matlab that splits the output into multiple files, one per class
# as required by matlab.
class enum_m_generator(basic_generator, generator_base):
"""
Special enum generator for matlab that splits the output into multiple files, one per class
as required by matlab.
"""
def __init__(self):
basic_generator.__init__(
self,
Expand Down
2 changes: 1 addition & 1 deletion gen/generators/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ def generate(self, input_filename):
self.model_cls = component.component
basic_generator.generate(self, component_model_path)

# Depend on the component model and any commands, data products, events models that the component uses:
def depends_on(self, input_filename):
"""Depend on the component model and any commands, data products, events models that the component uses."""
# Extract the name of the component from model file:
dirname, specific_name, component_name, *ignore = self._split_input_filename(
input_filename
Expand Down
4 changes: 2 additions & 2 deletions gen/models/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import os


# This model stores yaml information for configuring CodepEer for a directory.
class analyze(base):
"""This model stores yaml information for configuring CodepEer for a directory."""
def __init__(self, filename, template=os.environ["SCHEMAPATH"] + "/analyze.yaml"):
# Load the object from the file:
super(analyze, self).__init__(filename, template)

# Load component specific data structures with information from YAML file.
def load(self):
"""Load component specific data structures with information from YAML file."""
self.description = None
if "description" in self.data:
self.description = self.data["description"]
Expand Down
36 changes: 21 additions & 15 deletions gen/models/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
from collections import OrderedDict


# This is the object model for a packed array. It extracts data from a
# input file and stores the data as object member variables.
class array(type):
# Initialize the packed array object, ingest data, and check it by
# calling the base class init function.
"""
This is the object model for a packed array. It extracts data from a
input file and stores the data as object member variables.
"""
def __init__(self, filename):
"""
Initialize the packed array object, ingest data, and check it by
calling the base class init function.
"""
# Load the object from the file:
super(array, self).__init__(filename, os.environ["SCHEMAPATH"] + "/array.yaml")

# Load record specific data structures with information from YAML file.
def _load(self):
"""Load record specific data structures with information from YAML file."""
# Initialize object members:
self.length = None

Expand Down Expand Up @@ -140,8 +144,8 @@ def _load(self):
if self.is_atomic_type:
self.volatile_descriptor = "Atomic"

# Returns a flat ordered list of field objects that make up this array:
def flatten(self):
"""Returns a flat ordered list of field objects that make up this array."""
fields = []
for a_field in self.fields.values():
if a_field.is_packed_type:
Expand Down Expand Up @@ -210,16 +214,18 @@ def set_type_ranges(self, type_ranges_model):
self.element.literals = type_range.literals
self.element.type_ranges_loaded = True

# Returns true if the packed type is always valid, meaning running
# 'Valid on the element type will ALWAYS produce True. In other words, there
# is no bit representation of the type that could cause a constraint
# error when a range check is performed.
#
# To determine this we need to compare the type's type_range against
# the its bit layout (ie. format) to ensure that the maximum representable
# values in the format is also the maximum representable values in the
# type itself.
def is_always_valid(self):
"""
Returns true if the packed type is always valid, meaning running
'Valid on the element type will ALWAYS produce True. In other words, there
is no bit representation of the type that could cause a constraint
error when a range check is performed.
To determine this we need to compare the type's type_range against
the its bit layout (ie. format) to ensure that the maximum representable
values in the format is also the maximum representable values in the
type itself.
"""
# First we need to load the type ranges for this type:
self.load_type_ranges()
return self.element.is_always_valid()
Loading

0 comments on commit deeca6e

Please sign in to comment.