Skip to content

Commit b75a9de

Browse files
author
Jonathan Como
committed
Make ENV look like a constant and not raise when keys are missing
Closes #4
1 parent a0ab397 commit b75a9de

File tree

7 files changed

+64
-10
lines changed

7 files changed

+64
-10
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,27 @@ snake install # Installs dependencies
109109
snake build:app target={target} # Builds the tools
110110
snake build:tools [typ=core] # Builds the application
111111
```
112+
113+
## API Reference
114+
115+
### `@task(requires=None)`
116+
117+
Decorates a function that then exposes it as a task to be run.
118+
The name of the function becomes the name of the task.
119+
The `requires` parameter, if specified, is a list of strings where each string is the name of a task that this one depends on.
120+
121+
### `@namespace`
122+
123+
Decorates a function so that it exposes a task namespace.
124+
The name of the function becomes the name of the namespace.
125+
Tasks and nested namespaces can be defined in the namespace and will be called as `namespace:task`.
126+
127+
### `sh(command, silent=False)`
128+
129+
Runs a shell command.
130+
If silent is not specified, an exception will be raised if the resulting status of the command is nonzero.
131+
132+
### `ENV`
133+
134+
A dict that gives you access to environment variables.
135+
It has a special property that accessing by key using brackets will not raise a `KeyError` but will return `None` instead.

Snakefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
from snake import *
22
import os
33

4-
ENV = 'env'
4+
ENV_DIR = 'env'
55

66

77
@task
88
def bootstrap():
99
"""Bootstrap the virtualenv"""
10-
if not os.path.exists(ENV):
11-
sh('virtualenv -p python2.7 %s' % ENV)
10+
if not os.path.exists(ENV_DIR):
11+
sh('virtualenv -p python2.7 %s' % ENV_DIR)
1212

1313

1414
@task(requires=['bootstrap'])
1515
def install():
1616
"""Install development dependencies"""
17-
sh('%s/bin/pip install -r requirements.txt' % ENV)
17+
sh('%s/bin/pip install -r requirements.txt' % ENV_DIR)
1818

1919

2020
@namespace
@@ -23,7 +23,7 @@ def test():
2323
@task
2424
def current(test=''):
2525
"""Run tests in current virtualenv"""
26-
sh('%s/bin/nosetests -s %s' % (ENV, test))
26+
sh('%s/bin/nosetests -s %s' % (ENV_DIR, test))
2727

2828
@task
2929
def all():

snake/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .application import env, sh, task, namespace
1+
from .application import ENV, sh, task, namespace
22

3-
__all__ = ['env', 'sh', 'task', 'namespace']
3+
__all__ = ['ENV', 'sh', 'task', 'namespace']

snake/application.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from sys import exit, stderr, argv, exc_info
55
from traceback import extract_tb
66

7+
from .datastructures import LenientDict
78
from .parser import ApplicationArgsParser as parser
89
from .shell import ShellWrapper
910
from .tasks import TaskRegistry, NoSuchTaskException
@@ -104,7 +105,7 @@ def _execute_task(self, task, args):
104105
_instance = Application()
105106
_runner = ShellWrapper(_instance)
106107

107-
env = environ
108+
ENV = LenientDict(environ)
108109
sh = _runner.execute
109110
task = _instance.registry.add_task
110111
namespace = _instance.registry.add_namespace

snake/datastructures.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class LenientDict(dict):
2+
"""A class describing a dict that does not raise when using bracket notation on a key that
3+
does not exist. Behaves the same as a regular dict otherwise.
4+
"""
5+
def __getitem__(self, key):
6+
try:
7+
return super(LenientDict, self).__getitem__(key)
8+
except KeyError:
9+
pass

tests/datastructures_tests.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from unittest2 import TestCase
2+
3+
from snake.datastructures import LenientDict
4+
5+
6+
class LenientDictTestCase(TestCase):
7+
def test_it_acts_like_dict(self):
8+
d = LenientDict({'a': 1})
9+
d['b'] = 2
10+
11+
self.assertEqual(1, d['a'])
12+
self.assertEqual(2, d['b'])
13+
self.assertTrue('a' in d)
14+
self.assertFalse('c' in d)
15+
16+
def test_it_returns_nothing_instead_of_raising_for_key_error(self):
17+
d = LenientDict()
18+
19+
self.assertIsNone(d['a'])

tests/snake_tests.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,14 @@ def test_it_has_shortcut_to_current_environment(self):
139139
140140
@task
141141
def check():
142-
print(env.get('THING'))
142+
print(ENV['THING'])
143+
print(ENV['OTHER'])
143144
""")
144145

145146
result = self.execute('snake check')
146147

147148
self.assertStderrEmpty(result)
148-
self.assertStdoutEqual(result, ['hey'])
149+
self.assertStdoutEqual(result, ['hey', 'None'])
149150
self.assertStatusEqual(result, 0)
150151

151152
def test_it_exits_when_shell_command_fails(self):

0 commit comments

Comments
 (0)