Skip to content

Commit 6ebe123

Browse files
committed
update the tox
1 parent 809a513 commit 6ebe123

File tree

5 files changed

+204
-37
lines changed

5 files changed

+204
-37
lines changed

README.rst

+28-26
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ This README describes the steps involved in installing and running Optima. **Fol
1010

1111
0. Download and install the GitHub app (http://desktop.github.com) on Windows or Mac, or `sudo apt-get install git` on Linux.
1212

13-
0. Go to the Optima GitHub page and click on the small button next to "Download ZIP":
14-
![](http://optimamodel.com/figs/optima-github-button.png)
13+
0. Go to the Optima GitHub page and click on the small button next to "Download ZIP":
14+
![](http://optimamodel.com/figs/optima-github-button.png)
1515
(or use `git clone` if on Linux)
1616

1717
0. Finally, set up the Python path:
@@ -32,11 +32,11 @@ This README describes the steps involved in installing and running Optima. **Fol
3232

3333
## 2.1 Quick start installation
3434

35-
To install, run `python setup.py develop` in the root repository directory. This will add Optima to the system path. Optima can then be used via Python.
35+
To install, run `python setup.py develop` in the root repository directory. This will add Optima to the system path. Optima can then be used via Python.
3636

3737
To uninstall, run `python setup.py develop --uninstall`.
3838

39-
Note: do **not** use `python setup.py install`, as this will copy the source code into your system Python directory, and you won't be able to modify or update it easily.
39+
Note: do **not** use `python setup.py install`, as this will copy the source code into your system Python directory, and you won't be able to modify or update it easily.
4040

4141

4242
## 2.2 Detailed instructions
@@ -64,25 +64,25 @@ The last step is to make sure that Optima is available on the Python path. There
6464
0. Under the “Tools” (Linux and Windows) or “python” (under Mac) menu, go to “PYTHONPATH Manager”
6565
0. Select the Optima folder (e.g. `C:\Users\Alice\GitHub\Optima` on Windows) and click OK.
6666
0. **Option 2: modify system path**
67-
0. **Option 2A** (all operating systems): Go to the Optima root folder (in a terminal on Mac or Linux; in a command prompt [cmd.exe] in Windows) and run
68-
`python setup.py develop`
67+
0. **Option 2A** (all operating systems): Go to the Optima root folder (in a terminal on Mac or Linux; in a command prompt [cmd.exe] in Windows) and run
68+
`python setup.py develop`
6969
Note: if Spyder does not use the system Python (which can happen in some cases), this will not work. In this case:
70-
0. Inside a Spyder console, type
70+
0. Inside a Spyder console, type
7171
`import sys; sys.executable`
72-
0. Replace the above command with the location of this executable, e.g.
72+
0. Replace the above command with the location of this executable, e.g.
7373
`/software/anaconda/bin/python setup.py develop`
74-
0. **Option 2B** (Linux, Mac only): Add the Optima folder to `~/.bashrc` or `~/.bash_profile`, e.g.
75-
`export PYTHONPATH=$PYTHONPATH:/users/alice/github/optima`
74+
0. **Option 2B** (Linux, Mac only): Add the Optima folder to `~/.bashrc` or `~/.bash_profile`, e.g.
75+
`export PYTHONPATH=$PYTHONPATH:/users/alice/github/optima`
7676
[NB: if you don't use `bash`, you are probably a hacker and don't need these instructions.]
77-
0. **Option 2C** (Windows only): search for “variables” from the Start Menu; the option should be called something like “Edit environment variables for your account”. Under “user variables”, you should see “PYTHONPATH” listed. Add the folder for the Optima repository, e.g.
78-
`C:\Users\Alice\GitHub\Optima`
79-
If there are already things on the Python path, add this to the end separated by a semicolon and no space, e.g.
77+
0. **Option 2C** (Windows only): search for “variables” from the Start Menu; the option should be called something like “Edit environment variables for your account”. Under “user variables”, you should see “PYTHONPATH” listed. Add the folder for the Optima repository, e.g.
78+
`C:\Users\Alice\GitHub\Optima`
79+
If there are already things on the Python path, add this to the end separated by a semicolon and no space, e.g.
8080
`C:\Anaconda2\Library\bin;C:\Users\Alice\GitHub\Optima`
8181

8282
### 2.3 Verification/usage
8383

84-
If you followed the steps correctly, you should be able to run
85-
`import optima`
84+
If you followed the steps correctly, you should be able to run
85+
`import optima`
8686
from a Python console (either the system console or the Spyder console)
8787

8888
For usage examples, see the scripts in the `tests` folder. In particular, `testworkflow.py` shows a typical usage example.
@@ -101,7 +101,7 @@ On mac, install the `postgres` software with:
101101

102102
brew install postgres
103103

104-
On Linux, use
104+
On Linux, use
105105

106106
sudo apt-get install install postgres
107107

@@ -114,7 +114,7 @@ To run the `postgres` daemon in a terminal:
114114
```bash
115115
postgres -D /usr/local/var/postgresbrew
116116
```
117-
117+
118118
If you want to, you can run the `postgres` daemon with the Mac system daemon manager `launchctl`, or via the ruby wrapper for `lunchy`.
119119

120120

@@ -141,7 +141,7 @@ You will first need to install the python database migration tools:
141141
```bash
142142
pip install sqlalchemy-migrate psycopg2
143143
```
144-
144+
145145
Then to create the optima database, use these commands *from the root Optima directory* as `migrate` needs to find the migration scripts:
146146

147147
```bash
@@ -225,7 +225,9 @@ _On Mac_:
225225
lunchy start redis
226226
227227

228-
Run the server in two separate terminals. These scripts will start Python in a `virtualenv` isolated Python environments. First in one terminal:
228+
Run the server in two separate terminals. These scripts will start Python in a `virtualenv` isolated Python environments.
229+
If you wish to use system installed packages, append `--sitepackages` and it will not reinstall things that are already installed in the Python site packages.
230+
First in one terminal:
229231

230232
tox -e celery
231233

@@ -270,10 +272,10 @@ This section contains random pieces of wisdom we have encountered along the way.
270272
## 7.1 Workflows
271273

272274
- Make sure you pull and push from the repository regularly so you know what everyone else is doing, and everyone else knows what you're doing. If your branch is 378 commits behind develop, you're the sucker who's going to have to merge it.
273-
- There is very unclear advice about how to debug Python. It's actually fairly simple: if you run Python in interactive mode (e.g. via Spyder or via `python -i`), then if a script raises an exception, enter this in the console just after the crash:
274-
`import pdb; pdb.pm()`
275-
You will then be in a debugger right where the program crashed. Type `?` for available commands, but it works like how you would expect. Alternatively, if you want to effectively insert a breakpoint into your program, you can do this with
276-
`import pdb; pdb.set_trace()`
275+
- There is very unclear advice about how to debug Python. It's actually fairly simple: if you run Python in interactive mode (e.g. via Spyder or via `python -i`), then if a script raises an exception, enter this in the console just after the crash:
276+
`import pdb; pdb.pm()`
277+
You will then be in a debugger right where the program crashed. Type `?` for available commands, but it works like how you would expect. Alternatively, if you want to effectively insert a breakpoint into your program, you can do this with
278+
`import pdb; pdb.set_trace()`
277279
No one knows what these mysterious commands do. Just use them.
278280
- For benchmarking/profiling, you can use `tests/benchmarkmodel.py`. It's a good idea to run this and see if your changes have slowed things down considerably. It shows how to use the line profiler; Spyder also comes with a good function-level (but not line) profiler.
279281

@@ -291,7 +293,7 @@ def myfunc(args=None):
291293
if args is None: args = []
292294
print(args)
293295
```
294-
- It's dangerous to use `type()`; safer to use `isinstance()` (unless you _really_ mean `type()`). For example,
295-
`type(rand(1)[0])==float`
296-
is `False` because its type is `<type 'numpy.float64'>`; use `isinstance()` instead, e.g. `isinstance(rand(1)[0], (int, float))`
296+
- It's dangerous to use `type()`; safer to use `isinstance()` (unless you _really_ mean `type()`). For example,
297+
`type(rand(1)[0])==float`
298+
is `False` because its type is `<type 'numpy.float64'>`; use `isinstance()` instead, e.g. `isinstance(rand(1)[0], (int, float))`
297299
will catch anything that looks like a number, which is usually what you _really_ want.

bin/run_server.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sys
2+
import os
3+
4+
try:
5+
import server
6+
except:
7+
sys.path.insert(0, os.path.abspath("."))
8+
9+
from server import _autoreload
10+
from server import _twisted_wsgi
11+
12+
_autoreload.main(_twisted_wsgi.run)

server/_autoreload.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Autoreloading launcher.
2+
# Borrowed from Peter Hunt and the CherryPy project (http://www.cherrypy.org).
3+
# Some taken from Ian Bicking's Paste (http://pythonpaste.org/).
4+
# Adjustments made by Michael Elsdoerfer ([email protected]).
5+
#
6+
# Portions copyright (c) 2004, CherryPy Team ([email protected])
7+
# All rights reserved.
8+
#
9+
# Redistribution and use in source and binary forms, with or without modification,
10+
# are permitted provided that the following conditions are met:
11+
#
12+
# * Redistributions of source code must retain the above copyright notice,
13+
# this list of conditions and the following disclaimer.
14+
# * Redistributions in binary form must reproduce the above copyright notice,
15+
# this list of conditions and the following disclaimer in the documentation
16+
# and/or other materials provided with the distribution.
17+
# * Neither the name of the CherryPy Team nor the names of its contributors
18+
# may be used to endorse or promote products derived from this software
19+
# without specific prior written permission.
20+
#
21+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
import os, sys, time
33+
34+
try:
35+
import thread
36+
except ImportError:
37+
import dummy_thread as thread
38+
39+
# This import does nothing, but it's necessary to avoid some race conditions
40+
# in the threading module. See http://code.djangoproject.com/ticket/2330 .
41+
try:
42+
import threading
43+
except ImportError:
44+
pass
45+
46+
47+
RUN_RELOADER = True
48+
49+
_mtimes = {}
50+
_win = (sys.platform == "win32")
51+
52+
def code_changed():
53+
global _mtimes, _win
54+
for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())):
55+
if filename.endswith(".pyc") or filename.endswith(".pyo"):
56+
filename = filename[:-1]
57+
if not os.path.exists(filename):
58+
continue # File might be in an egg, so it can't be reloaded.
59+
stat = os.stat(filename)
60+
mtime = stat.st_mtime
61+
if _win:
62+
mtime -= stat.st_ctime
63+
if filename not in _mtimes:
64+
_mtimes[filename] = mtime
65+
continue
66+
if mtime != _mtimes[filename]:
67+
_mtimes = {}
68+
return True
69+
return False
70+
71+
def reloader_thread(softexit=False):
72+
"""If ``soft_exit`` is True, we use sys.exit(); otherwise ``os_exit``
73+
will be used to end the process.
74+
"""
75+
while RUN_RELOADER:
76+
if code_changed():
77+
# force reload
78+
if softexit:
79+
sys.exit(3)
80+
else:
81+
os._exit(3)
82+
time.sleep(1)
83+
84+
def restart_with_reloader():
85+
while True:
86+
args = [sys.executable] + sys.argv
87+
if sys.platform == "win32":
88+
args = ['"%s"' % arg for arg in args]
89+
new_environ = os.environ.copy()
90+
new_environ["RUN_MAIN"] = 'true'
91+
exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
92+
if exit_code != 3:
93+
return exit_code
94+
95+
def python_reloader(main_func, args, kwargs, check_in_thread=True):
96+
"""
97+
If ``check_in_thread`` is False, ``main_func`` will be run in a separate
98+
thread, and the code checker in the main thread. This was the original
99+
behavior of this module: I (Michael Elsdoerfer) changed the default
100+
to be the reverse: Code checker in thread, main func in main thread.
101+
This was necessary to make the thing work with Twisted
102+
(http://twistedmatrix.com/trac/ticket/4072).
103+
"""
104+
if os.environ.get("RUN_MAIN") == "true":
105+
if check_in_thread:
106+
thread.start_new_thread(reloader_thread, (), {'softexit': False})
107+
else:
108+
thread.start_new_thread(main_func, args, kwargs)
109+
110+
try:
111+
if not check_in_thread:
112+
reloader_thread(softexit=True)
113+
else:
114+
main_func(*args, **kwargs)
115+
except KeyboardInterrupt:
116+
pass
117+
else:
118+
try:
119+
sys.exit(restart_with_reloader())
120+
except KeyboardInterrupt:
121+
pass
122+
123+
def jython_reloader(main_func, args, kwargs):
124+
from _systemrestart import SystemRestart
125+
thread.start_new_thread(main_func, args)
126+
while True:
127+
if code_changed():
128+
raise SystemRestart
129+
time.sleep(1)
130+
131+
132+
def main(main_func, args=None, kwargs=None, **more_options):
133+
if args is None:
134+
args = ()
135+
if kwargs is None:
136+
kwargs = {}
137+
if sys.platform.startswith('java'):
138+
reloader = jython_reloader
139+
else:
140+
reloader = python_reloader
141+
reloader(main_func, args, kwargs, **more_options)

server/twisted_server.py server/_twisted_wsgi.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
from twisted.web.server import Site
99
from twisted.web.static import File
1010
from twisted.web.wsgi import WSGIResource
11+
from twisted.python.threadpool import ThreadPool
1112

1213
globalLogBeginner.beginLoggingTo([
1314
FileLogObserver(sys.stdout, lambda _: formatEvent(_) + "\n")])
1415

1516
import api
1617

17-
wsgi_app = WSGIResource(reactor, reactor.getThreadPool(), api.app)
18+
threadpool = ThreadPool(maxthreads=30)
19+
wsgi_app = WSGIResource(reactor, threadpool, api.app)
1820

1921
class OptimaResource(Resource):
2022
isLeaf = True
@@ -39,7 +41,15 @@ def render(self, request):
3941
except IndexError:
4042
port = "8080"
4143

42-
endpoint = serverFromString(reactor, "tcp:port=" + port)
43-
endpoint.listen(site)
4444

45-
reactor.run()
45+
def run():
46+
"""
47+
Run the server.
48+
"""
49+
endpoint = serverFromString(reactor, "tcp:port=" + port)
50+
endpoint.listen(site)
51+
52+
reactor.run()
53+
54+
if __name__ == "__main__":
55+
run()

tox.ini

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
[tox]
2-
envlist = runserver, celery
2+
envlist = runserver, runserver-prod, celery, celery-prod
33

4-
[testenv:runserver]
4+
[testenv]
55
changedir = {toxinidir}
6+
67
commands =
78
pip install -r server/requirements.txt
8-
python -m server.twisted_server {posargs}
9+
{runserver}: python bin/run_server.py {posargs}
10+
{celery}: celery -A server.webapp.tasks.celery_instance worker -l info
11+
12+
[testenv:runserver]
13+
skip_install = True
914

1015
[testenv:celery]
11-
changedir = {toxinidir}
12-
commands =
13-
pip install -r server/requirements.txt
14-
celery -A server.webapp.tasks.celery_instance worker -l info
16+
skip_install = True

0 commit comments

Comments
 (0)