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. 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.