Writing¶
Basics¶
Under the hood Calligraphy parses script source and tries to detect the language of any
given line. In addition, it looks for code wrapped in $(...) to inform it of a mixed
language line (with anything inside being Bash). After determining and annotating the
language for the script, Calligraphy then transpiles the script to pure Python and runs
it. This approach means that the rules for the language are fairly straightforward.
Rules¶
For the most part Calligraphy allows you to freely mix Bash and Python code, however there are a few rules that must be followed:
The base syntax is that of Python
Whenever you write Calligraphy code you’ll primarily be writing be writing Python code. For example, let’s say you want to create a program which searches for some text in a file. To do this, you’ll want a
searchfunction which takes in two arguments and prints out if the text is found or not.You’ll notice that other than a few parts this is stock Python code. All the base rules and syntax of Python apply except for the exceptions called out in the following rules.
Using
__name__ == '__main__'is disallowedAt this point in the development of Calligraphy, the
__name__property of the transpiled code isn’t yet overridden, so calling__name__in your main Calligraphy script won’t give you'__main__'. Changes to this behavior are currently on the roadmap, but until then this pattern is disallowed in the language.env,shell,shellopts,Options, andEnvironmentare reservedAll the normal Python keywords apply in Calligraphy, however three new ones have been added.
envis an object of theEnvironmentclass used to get/set environment variables so both of those are no longer available. In addition,shellis the name of the function used to execute shell commands in the transpiled code, so that is also protected. Finally, you cannot useshelloptsandOptionsas those are the variable/class used for setting shell options.Any lines written entirely in Bash will be executed in Bash
Calligraphy works by detecting the language for each line. If it finds that a line is written in Python, it will then check for inline bash. Otherwise, it registers the line as being Bash code and will execute it as such. For example, if we took the following code snippet:
cat test.txt | grep "foobar" > matches.txt with open('matches.txt') as f: matches = f.read().split('\n')
We can see that the first line would be executed as a shell command and the following lines would be executed as Python code. You can use the
--explainflag of the Calligraphy interpreter to show how Calligraphy has parsed your script/interpreted the languages.$(...)and?(...)denote inline BashA decent chunk of the time it isn’t super useful to pipe output to files and read them in so that you can access them from the Python side of things. To help with this issue you can wrap Bash code that’s on a Python line with
$(...)to tell Calligraphy that it’s a shell command. Calligraphy will return the contents of stdout when calls are wrapped in such a manner except for when it’s contained within an if statement. In that scenario Calligraphy will return the return code of the shell call performed. In addition, wrapping the Bash code with?(...)will act in the same manner as$(...)except it won’t print to stdout.$?will give you the return code of the last shell command to be runIf you need to check the RC of the previous shell command for whatever reason, you can reference it with
$?Use
env.NAMEto get/set environment variablesthe
envkeyword acts as a convenient alias to your local environment variables. You can access env variables withenv.NAMEand you can set variables withenv.NAME = 'value'. If an env variable doesn’t exist then it will returnNoneSetting environment variables from Bash lines is disallowed
While it’s not explicitly checked/disallowed, it will lead to improper language determination. Therefore, the Bash pattern of
NAME="value"is not permitted in the Calligraphy language. If you need to set and environment variable to the output of a shell command you can useenv.NAME = $(...)Other Calligraphy scripts can be imported via
source path/to/other/module.scriptFor example, if you used
source path/to/foo.scriptyou when then be able to usefoo.barto access the variablebarwithin thefoomodule. You can also usesource path/to/foo.script as bazto rename the import, meaning you would instead usebaz.barinstead offoo.barYou can set shell options using the
shelloptsvariable
In order to control shell options for your bash commands, you can use the
shelloptsbuilt-in variable. To do this, you’ll want to use something of the following form:shellopts.x = True shellopts.pipefail = True
If
shellopts.eis set then any non-if statement bash commands which have a non-zero RC will throw aRuntimeError
To make it easier to write scripts that behave properly, the
shellfunction will throw aRuntimeErrorwhen the following conditions are met:
shellopts.eis True (set by default in Calligraphy)The shell command is not inside an if statement
The shell command reports a non-zero RC