Architecture
Meco™ offers you extremely customizable architecture where you can implement your own components. A component essentially determines how Meco™ processes data in order to initialize an environment based on the request it receives.
Components
Meco™ has following components, which are run in the order they are listed below.
Request
Solver
Builder
Response
These components inherit relevant abstract class apart from Request
, which you can also customize.
Internal Mechanics
I would like to give the following code snippet in order to illustrate how the internal mechanics of Meco™ work.
import mMeco.mecoLib
meco = mMeco.mecoLib.Meco('--development main --app maya2020 --ignore-app-exec --raise-exceptions')
scriptFilePath = meco.writeFile()
print(scriptFilePath)
As you can see in the code snippet we initialize a development environment named main
including maya2020
app along with its environment. Though we ignore launching of Maya
executable by providing --ignore-app-exec
flag.
We'll get a raised exception since we provide --raise-exceptions
if any.
Lastly, we get the path of the script file path where the environment is written out.
When we execute the code, via the Python API or provided command, the following happen.
References:
Request (Component)
Since we provide request to the constructor of mMeco.mecoLib.Meco
as a str
instance,
mMeco.libs.requestLib.Request
parses it. If we didn't provide it, request would be parsed by
argparse.ArgumentParser
, which used by the request class itself. Entire parsed request is hold by the request class,
which we can refer back to.
Common Request is Checked
Meco™ stops execution and displays information if a common request is provided such as --version
, --about
.
Settings Operator is Initialized
Meco™ initializes the settings operator, meaning it reads settings from mMecoSettings
package in order to determine
what to use for the given request. For instance, --development main
flag and its value we provided get passed to the
settings module in order to get the path of the development environment.
Please note, Meco™ doesn't import these settings directly like import mMecoSettings.settingsLib
, which gives us
dynamic initialization advantages. This is also why I called it an operator
.
References:
Callback Operator is Initialized
Just like settings, callback operator is is initialized to be used later.
References:
App File Operator is Initialized
If -a
or --app
flag and a value along with them is provided just like we did, app file operator is initialized so
we can get information about the app when we need.
References:
Package Global Env Operator is Initialized
This operator holds global level information to initialize paths for packages.
References:
Solvers are Initialized
Available solvers are initialized.
Builders are Initialized
Available builders are initialized.
Responses are Initialized
Available responses are initialized.
Display Info
Meco™ stops execution and displays information if a request is provided with a flag such as --display-info
.
References:
Respond Last
Meco™ stops execution if --last
requested and provides previously created environment script if it exists for the
requested environment.
References:
Solver (Component)
Solve step is the step where Meco™ discovers packages from their respective environments based on the request.
Builder (Component)
Build step is where Meco™ builds environments including globally and locally built package environments.
Response (Component)
Response step is where Meco™ responds to the request it receives.
Implementing Custom Components
Solver
Solvers are responsible for discovering packages from their respective environments. They may also contain implementation to determine which packages should be initialized.
Solver classes inherit mMeco.abstract.solverAbs.Solver
abstract class, which contains built-in methods to help you out
discovering packages.
I've created an example mMeco.solvers.cacheReadSol.Solver
, which you can take a look at. In order to see it in action
you can run the following Python snippet.
import mMeco.mecoLib
m = mMeco.mecoLib.Meco('--raise-exceptions --cache-read')
m.execute()
The result would look like;
# Solver : cacheReadSol
# Builder: cacheReadBld
As you notice a solver cacheReadSol
as well as a builder cacheReadBld
are displayed because both of them
configured to run if -cr
or --cache-read
flag is provided.
This is accomplished in mMeco.mecoLib.Meco._getSolver
method. In this method we check whether -cr
flag is provided
via mMeco.libs.requestLib.Request
class instance. If so we return mMeco.solvers.cacheReadSol.Solver
instance,
which of course essentially will be used as the solver.
If you want to create a custom solver create a Python module like in the following example.
mMeco/solvers/<SOLVER_NAME>Sol.py
This module should contain a class named Solver
, which should inherit mMeco.abstract.solverAbs.Solver
abstract
class. Implement NAME
public static member and _solve
method to write your own solver.
In your _solve
method implementation you can use the following members as needed, which are instance of
mMeco.libs.envPathLib.EnvPath
class. This class has a method named listPackages
, which lists all the packages
for the environment that the class instance represents.
mMeco.abstract.solverAbs.Solver._reservedEnvPath
mMeco.abstract.solverAbs.Solver._developmentEnvPath
mMeco.abstract.solverAbs.Solver._stageEnvPath
mMeco.abstract.solverAbs.Solver._projectInternalEnvPath
mMeco.abstract.solverAbs.Solver._projectExternalEnvPath
mMeco.abstract.solverAbs.Solver._masterProjectInternalEnvPath
mMeco.abstract.solverAbs.Solver._masterProjectExternalEnvPath
Once you invoked the listPackages
method you can read and edit the list of the packages by invoking
mMeco.libs.envPathLib.EnvPath.packages
method. Other component will use the packages stored in this member of the
solver class.
You can also optionally implement the following methods.
mMeco.abstract.solverAbs.Solver._preSolve
mMeco.abstract.solverAbs.Solver._postSolve
Customize mMeco.libs.requestLib.Request
class by adding new flags so you can query them in
mMeco.mecoLib.Meco._getSolver
method in order to determine whether you should use your solver for a provided request.
Please check mMeco.mecoLib.Meco._getSolver
for example implementation.
You can also check mMeco.solvers.latestSol.Solver
for example solver implementation since
mMeco.solvers.cacheReadSol.Solver
doesn't do anything other than displaying the name.
References:
Builder
Builders are responsible for building the environments by using provided dedicated classes. They may also contain implementation to determine which packages should be included in the build.
Builder classes inherit mMeco.abstract.builderAbs.Builder
abstract class.
I've created an example mMeco.builders.cacheReadBld.Builder
, which you can take a look at. In order to see it in action
you can run the following Python snippet.
import mMeco.mecoLib
m = mMeco.mecoLib.Meco('--raise-exceptions --cache-read')
m.execute()
The result would look like;
# Solver : cacheReadSol
# Builder: cacheReadBld
This result should be familiar for you from the Solver section above.
Similar to the example above mMeco.mecoLib.Meco._getBuilder
method determines whether
mMeco.builders.cacheReadBld.Builder
should be used as the builder.
If you want to create a custom builder create a Python module like in the following example.
mMeco/builders/<BUILDER_NAME>Bld.py
This module should contain a class named Builder
, which should inherit mMeco.abstract.builderAbs.Builder
abstract
class. Implement NAME
public static member and _build
method to write your own builder.
Your implementation must populate the following members of the class as needed, so other components can use them.
Please note, these members are instance of list objects which must contain mMeco.libs.entryLib.EnvEntryContainer
class instances.
mMeco.abstract.builderAbs.Builder._preBuildEnvEntryContainers
mMeco.abstract.builderAbs.Builder._postBuildEnvEntryContainers
mMeco.abstract.builderAbs.Builder._reservedEnvEntryContainers
mMeco.abstract.builderAbs.Builder._developmentEnvEntryContainers
mMeco.abstract.builderAbs.Builder._stageEnvEntryContainers
mMeco.abstract.builderAbs.Builder._projectInternalEnvEntryContainers
mMeco.abstract.builderAbs.Builder._projectExternalEnvEntryContainers
mMeco.abstract.builderAbs.Builder._masterProjectInternalEnvEntryContainers
mMeco.abstract.builderAbs.Builder._masterProjectExternalEnvEntryContainers
You can also implement the following methods.
mMeco.abstract.builderAbs.Builder._preBuild
mMeco.abstract.builderAbs.Builder._postBuild
Customize mMeco.libs.requestLib.Request
class by adding new flags so you can query them in
mMeco.mecoLib.Meco._getBuilder
method in order to determine whether you should use your builder for a provided request.
Please check mMeco.mecoLib.Meco._getBuilder
for example implementation.
You can also check mMeco.builders.standardBld.Builder
for example builder implementation since
mMeco.builders.cacheReadBld.Builder
doesn't do anything other than displaying the name.
References:
Response
Responses are responsible for responding to the request. The response could be in any form you may need.
Response classes inherit mMeco.abstract.responseAbs.Response
abstract class.
I've created an example mMeco.responses.cacheWriteRes.Response
, which you can take a look at. In order to see it in
action you can run the following Python snippet.
import mMeco.mecoLib
m = mMeco.mecoLib.Meco('--raise-exceptions --cache-write')
m.execute()
The result would look like;
# Response: cacheWriteRes
This is accomplished like the other components, for builders mMeco.mecoLib.Meco._getResponse
method is used.
In this method we check whether --cache-write
flag is provided via mMeco.libs.requestLib.Request
class instance.
If so we return mMeco.responses.cacheWriteRes.Response
instance.
If you want to create a custom response create a Python module like in the following example.
mMeco/responses/<RESPONSE_NAME>Res.py
This module should contain a class named Response
, which should inherit mMeco.abstract.responseAbs.Response
abstract
class. Implement NAME
public static member and _respond
method to write your own response.
You can also implement the following methods.
mMeco.abstract.responseAbs.Response._preRespond
mMeco.abstract.responseAbs.Response._postRespond
Customize mMeco.libs.requestLib.Request
class by adding new flags so you can query them in
mMeco.mecoLib.Meco._getResponse
method in order to determine whether you should use your response for a provided
request. Please check mMeco.mecoLib.Meco._getResponse
for example implementation.
You can also check mMeco.responses.writeRes.Response
for example solver implementation since
mMeco.responses.cacheWriteRes.Response
doesn't do anything other than displaying the name.
References:
Recap
You can implement custom components, namely solvers, builders and responses and incorporate them with other components.
Add custom flags to mMeco.libs.requestLib.Request
class.
Customize the following methods as needed and use mMeco.libs.requestLib.Request
class to determine whether your
components should be used.
mMeco.mecoLib.Meco._getSolver
mMeco.mecoLib.Meco._getBuilder
mMeco.mecoLib.Meco._getBuilder
Add additional CLI as needed to accommodate your components. For instance, Meco™ invokes mMeco.mecoLib.Meco.writeFile
method when you initialize an environment via provided command on the command line.
You can take a look at the following examples.
mMeco.solvers.latestSol.Solver
mMeco.builders.standardBld.Builder
mMeco.responses.writeRes.Response
The following diagram illustrates the architecture.
All Library
All library is an instance of mMeco.libs.allLib.All
(singleton) class, which can be used to reach other libraries.
Please note, this class meant to be used to get information only. Do not set any data for any class instance contain
by this class and treat it as read-only.
Example usage.
# mMeco/mecoLib.py
class Meco(object):
# ...
def _getSolver(self):
# Meco class has a member named `allLib`, which is an instance of `mMeco.libs.allLib.All` class.
# So we can use it to get the instance of `mMeco.libs.requestLib.Request` class to determine
# whether `--cache-read` or `-cr` flag is provided. If this is the case we'll get the `cacheReadSol`
# by using `getByName` method of `_solverContainer` member.
if self._allLib.request().cacheRead():
return self._solverContainer.getByName(mMeco.solvers.cacheReadSol.Solver.NAME)
return self._solverContainer.getByName(mMeco.solvers.latestSol.Solver.NAME)
References:
Support
Please don't hesitate to contact me for any reason at all.