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

Python-генератор кода JS-дескрипторов на основе MADescriptionProvider #131

Merged
merged 13 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
b38c561
Абстрактный MADescriptionProvider, а также TestModelDescriptorProvide…
konstantin-kalinin-wizn-tech Jul 1, 2024
b6c8851
Реализация синглетона в базовом MADescriptionProvider
konstantin-kalinin-wizn-tech Jul 2, 2024
b7dae08
Заготовка модуля для транспайлера дескрипторов JS
konstantin-kalinin-wizn-tech Jul 2, 2024
7fb277b
Заготовка MADescriptionTranspiler_JS
konstantin-kalinin-wizn-tech Jul 2, 2024
cb37115
Рефакторинг MADescriptionProvider
konstantin-kalinin-wizn-tech Jul 2, 2024
d00487e
Параметризация MADescriptionTranspiler_JS
konstantin-kalinin-wizn-tech Jul 3, 2024
a762b75
Синглетонность и рефакторинг MADescriptionProvider
konstantin-kalinin-wizn-tech Jul 3, 2024
d591f09
Правки кодогенерации в MADescriptionProvider
konstantin-kalinin-wizn-tech Jul 8, 2024
a36c1e3
Добавлены недостающие поля дескрипторов
konstantin-kalinin-wizn-tech Jul 8, 2024
84ff1b2
MAMetaSingleton вместо MADescriptionProviderMetaSingleton
konstantin-kalinin-wizn-tech Jul 9, 2024
d34e840
Декоратор @abstractmethod
konstantin-kalinin-wizn-tech Jul 9, 2024
ea69376
Правки MADescriptionTranspiler_JS (динамическое вычисление импортов, …
konstantin-kalinin-wizn-tech Jul 9, 2024
b29da98
Использование фактического класса контейнера вместо захардкоженого MA…
konstantin-kalinin-wizn-tech Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions Magritte/descriptions/MADescriptionProvider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

from abc import abstractmethod
from Magritte.descriptions.MADescription_class import MADescription


class MAMetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
singleton_key = 'singleton'
if singleton_key in kwargs:
singleton = bool(kwargs.pop(singleton_key))
else:
singleton = True # The default
if singleton:
if cls not in cls._instances:
cls._instances[cls] = super(MAMetaSingleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
else:
return super(MAMetaSingleton, cls).__call__(*args, **kwargs)

class MADescriptionProvider(metaclass=MAMetaSingleton):

def __init__(self):
self._all_descriptions = list()
self._descriptions_by_model_name = dict()
self.instatiate_descriptions()

def register_description(self, aDescription: MADescription):
"""
Should be called inside instatiate_descriptions only.
Should be called only after a name is assigned to a description.
"""
model_name = aDescription.name
if model_name is None:
raise TypeError('register_description should be called only after a name is assigned to a description')
if model_name in self._descriptions_by_model_name:
return
self._descriptions_by_model_name[model_name] = aDescription
self._all_descriptions.append(aDescription)

@abstractmethod
def instatiate_descriptions(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

насколько я понимаю, в Python абстрактные методы принято обозначать декоратором abstractmethod из модуля abc ("abstract base class").

Документация: https://docs.python.org/3/library/abc.html

Пример: https://www.geeksforgeeks.org/abstract-classes-in-python/

"""Method to generate the descriptions."""
pass

@property
def all_descriptions(self) -> list[MADescription]:
"""Returns all descriptions."""
return self._all_descriptions

def description_for(self, model_name: str) -> MADescription:
"""Returns a model description for the given model type."""
if model_name in self._descriptions_by_model_name:
return self._descriptions_by_model_name[model_name]
raise ValueError(f'Unknown model type: {model_name}')
5 changes: 3 additions & 2 deletions Magritte/descriptions/tests/ReferenceAcyclic_Example.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

from Magritte.model_for_tests.EnvironmentProvider_test import TestEnvironmentProvider
from Magritte.model_for_tests.ModelDescriptor_test import TestModelDescriptor
from Magritte.model_for_tests.ModelDescriptor_test import TestModelDescriptorProvider
from Magritte.visitors.MAVisitor_class import MAVisitor


Expand Down Expand Up @@ -39,7 +39,8 @@ def visitReferenceDescription(self, anObject):

def main():
provider = TestEnvironmentProvider()
hostDescriptor = TestModelDescriptor.description_for("Host")
descriptors = TestModelDescriptorProvider()
hostDescriptor = descriptors.description_for("Host")
#print(hostDescriptor)
#print(hostDescriptor.acyclicDescription)
acyclicTestVisitor = AcyclicTestVisitor()
Expand Down
139 changes: 67 additions & 72 deletions Magritte/model_for_tests/ModelDescriptor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from Magritte.descriptions.MAContainer_class import MAContainer
from Magritte.descriptions.MADateAndTimeDescription_class import MADateAndTimeDescription
from Magritte.descriptions.MADateDescription_class import MADateDescription
from Magritte.descriptions.MADescription_class import MADescription
from Magritte.descriptions.MAIntDescription_class import MAIntDescription
from Magritte.descriptions.MAStringDescription_class import MAStringDescription
from Magritte.descriptions.MAToManyRelationDescription_class import MAToManyRelationDescription
from Magritte.descriptions.MAToOneRelationDescription_class import MAToOneRelationDescription
from Magritte.descriptions.MASingleOptionDescription_class import MASingleOptionDescription
from Magritte.descriptions.MADescriptionProvider import MADescriptionProvider
from Magritte.accessors.MAAttrAccessor_class import MAAttrAccessor
from Magritte.model_for_tests.Organization import Organization
from Magritte.model_for_tests.Account import Account
Expand All @@ -20,11 +22,9 @@
from Magritte.model_for_tests.SoftwarePackage import SoftwarePackage


class TestModelDescriptor:
@classmethod
def description_for(cls, model_type: str) -> MAContainer:
"""Returns a fact description for the given fact type."""
class TestModelDescriptorProvider(MADescriptionProvider):

def instatiate_descriptions(self):
subscription_plan_desc_container = MAContainer()
user_desc_container = MAContainer()
org_desc_container = MAContainer()
Expand All @@ -40,12 +40,12 @@ def description_for(cls, model_type: str) -> MAContainer:
[
MAStringDescription(
name='name', label='Name', required=True, accessor=MAAttrAccessor('name')
),
),
MAStringDescription(
name='version', label='Version', required=True, accessor=MAAttrAccessor('version')
),
]
)
),
]
)

subscription_plan_desc_container.kind = SubscriptionPlan
subscription_plan_desc_container.name = 'SubscriptionPlan'
Expand All @@ -58,7 +58,7 @@ def description_for(cls, model_type: str) -> MAContainer:
),
MAIntDescription(
name='price', label='Price (per month)', required=True, accessor=MAAttrAccessor('price'),

),
MAStringDescription(
name='description', label='Description of the plan features', required=False,
Expand All @@ -75,49 +75,49 @@ def description_for(cls, model_type: str) -> MAContainer:
MAStringDescription(
name='regnum', label='RegNum', required=True, accessor=MAAttrAccessor('regnum'),
sa_isPrimaryKey=True, sa_attrName='_regnum',
),
),
MAStringDescription(
name='fio', label='FIO', required=True, accessor=MAAttrAccessor('fio'), sa_attrName='_fio',
),
),
MADateDescription(
name='dateofbirth', label='DateOfBirth',
required=True, accessor=MAAttrAccessor('dateofbirth'),
sa_attrName='_dateofbirth',
),
),
MAStringDescription(
name='gender', label='Gender', required=True, accessor=MAAttrAccessor('gender'),
sa_attrName='_gender',
),
),
MAToOneRelationDescription(
name='organization', label='Organization', required=True,
accessor=MAAttrAccessor('organization'), classes=[Organization],
reference=org_desc_container,
),
),
MADateDescription(
name='dateofadmission', label='DateOfAdmission',
required=True, accessor=MAAttrAccessor('dateofadmission'), sa_attrName='_dateofadmission',
),
),
MADateDescription(
name='dateofdeparture', label='DateOfDeparture',
required=False, accessor=MAAttrAccessor('dateofdeparture'), sa_attrName='_dateofdeparture',
),
),
MAToManyRelationDescription(
name='setofaccounts', label='SetOfAccounts', required=True,
accessor=MAAttrAccessor('setofaccounts'), classes=[Account],
reference=acc_desc_container,
),
),
MASingleOptionDescription(
name='plan', label='Subscription Plan', required=False, accessor=MAAttrAccessor('plan'),
options=SubscriptionPlan.entries,
options=SubscriptionPlan.entries(),
reference=subscription_plan_desc_container
),
#MAToOneRelationDescription(
),
# MAToOneRelationDescription(
# name='plan', label='Subscription Plan', required=False,
# accessor=MAAttrAccessor('plan'), classes=[SubscriptionPlan],
# reference=subscription_plan_desc_container,
# ),
]
)
]
)

org_desc_container.kind = Organization
org_desc_container.name = 'Organization'
Expand All @@ -127,27 +127,27 @@ def description_for(cls, model_type: str) -> MAContainer:
MAStringDescription(
name='name', label='Name', required=True, accessor=MAAttrAccessor('name'),
sa_isPrimaryKey=True, sa_attrName='_name',
),
),
MAStringDescription(
name='address', label='Address', required=True, accessor=MAAttrAccessor('address'),
sa_attrName='_address',
),
),
MABooleanDescription(
name='active', label='Active', required=True, accessor=MAAttrAccessor('active'),
sa_attrName='_active',
),
),
MAToManyRelationDescription(
name='listusers', label='List of Users', required=True,
accessor=MAAttrAccessor('listusers'), classes=[User],
reference=user_desc_container,
),
),
MAToManyRelationDescription(
name='listcomp', label='List of Computers', required=True,
accessor=MAAttrAccessor('listcomp'), classes=[Host],
reference=host_desc_container,
),
]
)
),
]
)

acc_desc_container.kind = Account
acc_desc_container.name = 'Account'
Expand All @@ -157,33 +157,33 @@ def description_for(cls, model_type: str) -> MAContainer:
MAStringDescription(
name='login', label='Login', required=True, accessor=MAAttrAccessor('login'),
sa_isPrimaryKey=True, sa_attrName='_login',
),
#!TODO Change to MAPasswordDescription when it is implemented
),
# !TODO Change to MAPasswordDescription when it is implemented
MAStringDescription(
name='password', label='Password', required=True, accessor=MAAttrAccessor('password'),
sa_attrName='_password',
),
),
# !TODO Change to MAPasswordDescription when it is implemented
MAStringDescription(
name='ntlm', label='NTLM', accessor=MAAttrAccessor('ntlm'),
sa_attrName='_ntlm',
),
),
MADateAndTimeDescription(
name='reg_timestamp', label='Timestamp Of Registration',
required=True, accessor=MAAttrAccessor('reg_timestamp'),
sa_attrName='_reg_timestamp',
),
),
MAIntDescription(
name='days', label='Days valid', required=True, accessor=MAAttrAccessor('days'),
sa_attrName='_days',
),
),
MAToOneRelationDescription(
name='port', label='Port', required=True,
accessor=MAAttrAccessor('port'), classes=[Port],
reference=port_desc_container,
),
]
)
),
]
)

host_desc_container.kind = Host
host_desc_container.name = 'Host'
Expand All @@ -193,21 +193,21 @@ def description_for(cls, model_type: str) -> MAContainer:
MAStringDescription(
name='ip', label='IP Address', required=True, accessor=MAAttrAccessor('ip'),
sa_isPrimaryKey=True, sa_attrName='_ip',
),
),
MAToManyRelationDescription(
name='ports', label='Ports', required=True,
accessor=MAAttrAccessor('ports'), classes=[Port],
reference=port_desc_container,
sa_attrName='_ports',
),
),
MAToManyRelationDescription(
name='software', label='Software', required=True,
accessor=MAAttrAccessor('software'), classes=[SoftwarePackage],
reference=soft_desc_container,
sa_attrName='_software',
),
]
)
),
]
)

port_desc_container.kind = Port
port_desc_container.name = 'Port'
Expand All @@ -217,52 +217,47 @@ def description_for(cls, model_type: str) -> MAContainer:
MAIntDescription(
name='numofport', label='Number of Port', required=True, accessor=MAAttrAccessor('numofport'),
sa_attrName='_numofport',
),
),
MAToOneRelationDescription(
name='host', label='Host', required=True,
accessor=MAAttrAccessor('host'), classes=[Host], reference=host_desc_container,
sa_attrName='_host',
),
),
MASingleOptionDescription(
name='status', label='Status', required=False, accessor=MAAttrAccessor('status'),
options=Port.STATUSES,
reference=MAStringDescription(),
sa_attrName='_status'
),
),
MAStringDescription(
accessor = MAAttrAccessor('label'), readOnly = True
),
]
)
accessor=MAAttrAccessor('label'), readOnly=True
),
]
)

self.register_description(subscription_plan_desc_container)
self.register_description(subscription_plan_desc_container)
self.register_description(user_desc_container)
self.register_description(org_desc_container)
self.register_description(acc_desc_container)
self.register_description(host_desc_container)
self.register_description(port_desc_container)
self.register_description(soft_desc_container)

if model_type == 'User':
return user_desc_container
elif model_type == 'Organization':
return org_desc_container
elif model_type == 'Account':
return acc_desc_container
elif model_type == 'Host':
return host_desc_container
elif model_type == 'Port':
return port_desc_container
elif model_type == 'SubscriptionPlan':
return subscription_plan_desc_container
elif model_type == 'SoftwarePackage':
return soft_desc_container
else:
raise ValueError(f"Unknown model type: {model_type}")


if __name__ == "__main__":

from Magritte.model_for_tests.EnvironmentProvider_test import TestEnvironmentProvider

org_desc = TestModelDescriptor.description_for("Organization")
user_desc = TestModelDescriptor.description_for("User")
acc_desc = TestModelDescriptor.description_for("Account")
host_desc = TestModelDescriptor.description_for("Host")
port_desc = TestModelDescriptor.description_for("Port")
soft_desc = TestModelDescriptor.description_for("SoftwarePackage")
descriptors = TestModelDescriptorProvider()

org_desc = descriptors.description_for("Organization")
user_desc = descriptors.description_for("User")
acc_desc = descriptors.description_for("Account")
host_desc = descriptors.description_for("Host")
port_desc = descriptors.description_for("Port")
soft_desc = descriptors.description_for("SoftwarePackage")

test_env_provider = TestEnvironmentProvider()
org = test_env_provider.organization
Expand Down
10 changes: 6 additions & 4 deletions Magritte/visitors/MAReferencedDataWriterReader_visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,17 +609,19 @@ def deserializeHumanReadable(self, serialized_str: str, description: MADescripti
if __name__ == "__main__":

from Magritte.model_for_tests.EnvironmentProvider_test import TestEnvironmentProvider
from Magritte.model_for_tests.ModelDescriptor_test import TestModelDescriptor, Host, Port, User
from Magritte.model_for_tests.ModelDescriptor_test import TestModelDescriptorProvider, Host, Port, User

provider = TestEnvironmentProvider()
descriptors = TestModelDescriptorProvider()

host = provider.hosts[0]
hostDescriptor = TestModelDescriptor.description_for(Host.__name__)
hostDescriptor = descriptors.description_for(Host.__name__)

port = host.ports[5]
portDescriptor = TestModelDescriptor.description_for(Port.__name__)
portDescriptor = descriptors.description_for(Port.__name__)

user = provider.users[0]
userDescriptor = TestModelDescriptor.description_for(User.__name__)
userDescriptor = descriptors.description_for(User.__name__)

ipDescriptor = hostDescriptor.children[0]
portsDescriptor = hostDescriptor.children[1]
Expand Down
Loading