Skip to content
David Foster edited this page Aug 15, 2023 · 11 revisions

Crystal can be launched with an interactive Python shell. This allows you to perform advanced manipulations on a project manually, and to manipulate projects with automated scripts.

Tell Crystal to open a shell by passing the --shell CLI option:

$ crystal --shell
Crystal 1.5.0b (Python 3.8.10)
Type "help" for more information.
Variables "project" and "window" are available.
Use exit() or Ctrl-D (i.e. EOF) to exit.
>>> 

After a project is opened, you can manipulate it in the shell:

>>> project
<crystal.model.Project object at 0x115a61bb0>
>>> help(project)  # display interactive help

>>> list(project.root_resources)
[RootResource('Home Page','https://xkcd.com/')]

>>> list(project.resource_groups)
[ResourceGroup('Comic','https://xkcd.com/#/')]

>>> len(project.resources)
16313
>>> list(project.resources)[0]
Resource('https://xkcd.com/')

>>> first_comic = project.get_resource('https://xkcd.com/1/')
>>> first_comic
Resource('https://xkcd.com/1/')

>>> list(first_comic.revisions())
[<ResourceRevision 14 for 'https://xkcd.com/1/'>]
>>> first_comic.default_revision()
<ResourceRevision 14 for 'https://xkcd.com/1/'>

>>> project.get_resource('https://xkcd.com/99/')
>>> # not in project
>>> from crystal.model import Resource
>>> some_comic = Resource(project, 'https://xkcd.com/99/')
>>> some_comic
Resource('https://xkcd.com/99/')

>>> list(some_comic.revisions())
[]
>>> some_comic.default_revision()
>>> # no default revision
>>> revision_future = some_comic.download()
>>> revision = revision_future.result()  # keep result() separate from download(); will deadlock otherwise
>>> revision
<ResourceRevision 18 for 'https://xkcd.com/99/'>
>>> import pprint
>>> pprint.pprint(revision.metadata)
{'headers': [['Connection', 'keep-alive'],
             ['Content-Length', '8297'],
             ['Server', 'nginx'],
             ['Content-Type', 'text/html; charset=UTF-8'],
             ['Last-Modified', 'Sat, 16 Jul 2022 01:52:42 GMT'],
             ['ETag', '"62d219ea-2069"'],
             ['Expires', 'Mon, 18 Jul 2022 15:18:54 GMT'],
             ['Cache-Control', 'max-age=300'],
             ['Via', '1.1 varnish, 1.1 varnish'],
             ['Accept-Ranges', 'bytes'],
             ['Date', 'Mon, 18 Jul 2022 15:13:54 GMT'],
             ['Age', '0'],
             ['X-Served-By', 'cache-dfw18663-DFW, cache-bfi-krnt7300049-BFI'],
             ['X-Cache', 'MISS, MISS'],
             ['X-Cache-Hits', '0, 0'],
             ['X-Timer', 'S1658157234.020196,VS0,VE58'],
             ['Vary', 'Accept-Encoding']],
 'http_version': 11,
 'reason_phrase': 'OK',
 'status_code': 200}
>>> with revision.open() as f:
...     body = f.read()
>>> body[:141]
b'<!DOCTYPE html>\n<html>\n<head>\n<link rel="stylesheet" type="text/css" href="/s/7d94e0.css" title="Default"/>\n<title>xkcd: Binary Heart</title>'

When you're done, exit the shell by pressing Ctrl-D (on macOS and Linux), Ctrl-Z and Return (on Windows), or by running the exit() command:

>>> ^D
now waiting for main window to close...

Shell API

There is no officially supported API on the shell at the moment. If you want to write automated scripts that run in the shell, avoid using methods/attributes whose name starts with an underscore (_) because those methods/attributes are private or unstable.

$PYTHONSTARTUP

If you start Crystal with the PYTHONSTARTUP environment variable set to the filepath of a Python .py file, that file will be run in the shell when Crystal starts. That can be useful for automatically importing certain modules or defining certain variables.

For example, if the file startup.py exists in the current directory that looks like:

# startup.py
from crystal.model import *
EXCLUDED_URLS = ['a', 'b', 'c']

And you start Crystal using:

$ PYTHONSTARTUP=startup.py crystal --shell

You can access items imported from crystal.model and the EXCLUDED_URLS variable defined by startup.py:

>>> Resource
<class 'crystal.model.Resource'>
>>> EXCLUDED_URLS
['a', 'b', 'c']
>>>
Clone this wiki locally