Create your first projects and commands
Welcome to the first tutorial. This tutorial will walk you through the creation of a container that will hold all your projects. We will create one project using socon admin command and we will create commands for that specific project.
Install Socon
If you didn’t install Socon yet, check the install section.
Creating a container
To start with Socon, we will have to take care of some initial setup. The first thing to do is to create a Socon container – a collection of settings that include Socon-specific options, project-specific settings, environment configuration and many others.
Before anything else, use cd to get inside a folder where you want your
code to be stored. Then:
$ socon createcontainer tutorial
You can also use:
$ socon createcontainer tutorial
Using of the above commands will create a folder tutorial
Warning
To avoid any kind of issue, do not name your folder with any of the built-in Python or Socon components.
Note
You can add --target to specify the path where the container will be
created. If the target folder already exist, it will be populated by
Socon files, otherwise if the folder does not exist it will be created.
Now that we have create a container. Let’s look at what’s inside:
tutorial/
tutorial/
__init__.py
settings.py
projects/
__init__.py
manage.py
These files are:
The outer
tutorial/root directory is the container for your projects. The name doesn’t matter, you can choose what you like.manage.py: A command-line utility that lets you interact with your projects in various ways. You can read all the details aboutmanage.pyin socon and manage.py.tutorial/settings.py: Settings/configuration for this Socon project. It’s the global settings file for all your projects in the container.
Creating a project
At this point you cannot really interact with Socon as you haven’t yet registered a project. To do so, we will need to create one project and register it in the global settings.
Each project you write in Socon consists of a python package that follows a certain package convention. Socon comes with admin command that help automatically generate the basic directory structure of a project. Doing so you can focus on writing code and not creating directories.
In this tutorial, we’ll create our first project in the projects directory.
To create your project, be sure to be in the tutorial container.
$ python manage.py createproject artemis
Note
For this command there is also a possibility to add --target to the
commands. However this one is trickier. Socon will check that the destination
is in the projects folder otherwise it will throw you an error.
If you don’t use this option, you must be in a container to create the project.
It doesn’t matter if this project already exists or not.
Now that we have created a project. Let’s look at what’s inside:
artemis/
__init__.py
management/
__init__.py
config.py
projects.py
These files are:
The outer
artemis/root directory is your project that will hold it’s configuration and every command, hook and manager that you will create during your project lifetime.artemis/projects.py: Configure your project in this file for example it’s name, the path to it’s configuration file and more.artemis/management/config.py: The configuration file for your project. This acts the same way astutorial/settings.pybut will only be visible for this particular project.
Register your first command
Register your project
Now that we have created our project, we will be able to create our first
command. But before that we need to register our project in the settings that you
can find in tutorial/management/settings.py. You can find more
about settings here.
Replace INSTALLED_PROJECTS by this one:
INSTALLED_PROJECTS = [
'projects.artemis'
]
What we just did allows Socon to see your project and all the commands that we are going to create.
Create the launch command
First thing is to create a management/commands directory inside the
artemis project:
$ cd projects/artemis
$ mkdir -p management/commands
We can then create a file in that folder that will hold our commands. The name of the file does not have any importance but for the sake of the tutorial we will use the same name as our command.
$ touch management/commands/__init__.py
$ touch management/commands/launch.py
Note
You will notice that we are using linux command to create the files/folders. Socon works perfectly on Windows so I will let you adapt the commands for your operating system.
You should have something looking like this in the overall project:
tutorial/
tutorial/
projects/
__init__.py
artemis/
management/
__init__.py
commands/
__init__.py
launch.py
config.py
...
...
Now open the launch.py command file and copy the following.
from socon.core.management.base import ProjectCommand, Config
from socon.core.registry.base import ProjectConfig
class LaunchCommand(ProjectCommand):
name = 'launch'
def handle(self, config: Config, project_config: ProjectConfig):
print('Launching the Orion SpaceCraft to the moon')
Let’s explain what is going on in this file. We first need to import the
ProjectCommand class.
This is really important. In Socon there is only two class for the commands.
ProjectCommand: commands that will load aProjectConfigto access all the configuration of the project. This command, if declared inside a project will only be accessible there. If the project command is defined in thecommonenvironment (we will see later what that means) it will be accessible for every projects. This command must be called using the--projectoption.BaseCommand: commands that is general for all projects. These commands can be declared anywhere and does not depend on any project. These commands won’t load anyProjectConfig
Note
If you want to lean more about commands and how they work, check the commands reference.
Then we need to define the subclass LaunchCommand. The name of the class
is important only if you don’t want to set the
name attribute. Socon
will only keep the part before Command. Which mean it’s name would
have been the same as the one we defined above.
Note
If for a specific reason, you want to rename your command, you can do that
by overriding the name attribute.
The handle() method. This method must be
implemented if you don’t want to have a nice error thrown at you. It is the starting
point of the command. From here you can define any behavior for that command.
We kept it simple for the example.
Check your helper
Before running our command we can quickly check that the command has been well registered by running the help command:
$ python manage.py help
You should have the following output:
...
Common commands
---------------
[core]
createcontainer (G)
createplugin (G)
createproject (G)
Projects commands
-----------------
[artemis]
launch (P)
You can see in the helper what kind of commands are registered and for which
projects. You can see the core commands that we used and the artemis project
with our launch command.
Note
A (P) and a (G) next to the commands specify the type of command. This
quickly tells you if you need to specify the --project or not.
Run the command
Well, I guess it’s time to run the command to launch our rocket. For that it’s pretty simple:
$ python manage.py launch --project artemis
Note
Remember it’s a project command. We need to specify the project as we are
using a ProjectCommand.
At this time you should see:
Launching the Orion SpaceCraft to the moon
Congratulations! You launched your first rocket ;)
Going further
Add command arguments
Imagine that you want to add an argument to your command. Well Socon implemented
an easy way to do that using the add_arguments()
method inside the LaunchCommand subclass.
Let’s add it.
from time import sleep
from argparse import ArgumentParser
from socon.core.management.base import ProjectCommand, Config
from socon.core.registry.base import ProjectConfig
class LaunchCommand(ProjectCommand):
name = 'launch'
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument('--countdown', help=(
"Countdown before we launch the rocket"
), default=0)
def handle(self, config: Config, project_config: ProjectConfig):
countdown = config.getoption('countdown')
for i in range(int(countdown), 0, -1):
print(i)
sleep(1)
print('Launching the Orion SpaceCraft to the moon')
We are using the parser parameter to add new arguments to the command.
The parser is an instance of the ArgumentParser class. It is passed to this
method to extend the legacy arguments of the ProjectCommand.
If you want to see available arguments for this command:
$ python manage.py launch --project artemis --help
The config object which is an instance of the Config class
give you access to the arguments passed to the commands using the
getoption() method. The config object contain
also the options Namespace. This allow you to access the argument using only
config.options.countdown.
Use project config and general settings
Now let’s imagine that you want to use a setting that is common to all you projects and one setting that is project specific. To do so we will need to edit two different files.
The tutorial/settings.py which is common to all projects:
# My beautifull country
COUNTRY = 'France'
Warning
You can put everything in this file but only UPPERCASE variable will be registered as global settings.
The projects/artemis/management/config.py which is specific to the
artemis project:
# The SpaceCraft name
SPACECRAFT = 'Orion'
To use them it’s pretty simple:
from socon.core.management.base import ProjectCommand, Config
from socon.core.registry.base import ProjectConfig
from socon.conf import settings
class LaunchCommand(ProjectCommand):
name = 'launch'
def handle(self, config: Config, project_config: ProjectConfig):
country = settings.COUNTRY
spacecraft = project_config.get_setting('SPACECRAFT')
print(f'Launching the {spacecraft} SpaceCraft to the moon from {country}')
Each settings defined in tutorial/settings.py will be accessible as an attribute
of the settings object from socon.core.settings.
Each settings defined in the project configuration (e.g projects/artemis/management/config.py)
will be accessible from the project_config object and its
get_setting() method.
Now if you start the command, you should see the following:
Launching the Orion SpaceCraft to the moon from France
General command
General command are similar to project command. The major notable difference
is that the handle() method does not contain
the ProjectConfig object. Even if that is true,
you will notice that you can still pass the --project
argument to the general command because project can override general command
behavior if required. We will cover that in the next tutorial:
Change the behavior of a command.
A quick example on how to define a BaseCommand.
from socon.core.management.base import BaseCommand, Config
from socon.core.registry.base import ProjectConfig
class PublishCommand(BaseCommand):
name = 'publish'
def handle(self, config: Config):
print('Publish an article about Nasa')
Common space
The common space is where we will define everything the might or might not
be common to all projects. Some might have already guessed but the common space
directory is the tutorial folder that contain the settings.py file:
tutorial/
tutorial/ (common space)
__init__.py
settings.py
projects
manage.py
It’s generally here that we will define general and project commands if we want them to be available for every projects. To see how this works, let’s create a new project and add two new commands to it.
$ python manage.py createproject apollo
Warning
Don’t forget to add this new project to the general settings!
Then let’s create two new commands. This time we will create them in the common
space. For that you will have to create the management/commands
folder with the two commands:
tutorial/
tutorial/
__init__.py
management/
__init__.py
commands/
__init__.py
deploy.py
build.py
settings.py
...
The deploy.py command file
from socon.core.management.base import BaseCommand, Config
class DeployCommand(BaseCommand):
name = 'deploy'
def handle(self, config: Config):
print("Deploy satellites and other payloads")
The build.py command file
from socon.core.management.base import ProjectCommand, Config
from socon.core.registry.base import ProjectConfig
class BuildCommand(ProjectCommand):
name = 'build'
def handle(self, config: Config, project_config: ProjectConfig):
print(f"Building {project_config.get_setting('SPACECRAFT')}")
Now things are getting really interesting. If you get the helper from the
manage.py python manage.py help you will see in the common
section a new line with [tutorial]. This is where your common
commands are stored. They are available for all you projects. To demonstrate that, we will
try to start the previous launch command for the new project apollo:
$ python manage.py launch --project apollo
Doing so, you will see a nice error telling you that it is an unknown command and
that the only project that contains this command is artemis.
Now let’s do the same with the new project command build for the two projects.
Warning
You will need to add SPACECRAFT = Saturn IB to the apollo config file.
Otherwise Socon will throw you an error because the variable does not exist.
You can try by yourself if you want to see what it does without the variable.
python manage.py build --project apollo
python manage.py build --project artemis
Each commands will get you the following results:
Building Saturn IB
Building Orion
As you can see both projects have access to these commands. Now we can
do the same with the general command. However, as it does not depend on any
project we don’t have to specify the --project option.
$ python manage.py deploy.py
Doing do so the output will be:
Deploy satellites and other payloads
Congratulations! You have reached the end of the first tutorial. In the next tutorial Change the behavior of a command we will learn how to change the behavior of a project/general command in a specific project.