diff --git a/.gitignore b/.gitignore index 686c336..eb9c60a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /config.json *.pyc .idea +lib diff --git a/README.md b/README.md index 0a6f8ab..ccf83ce 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,55 @@ $ vagrant ssh $ cd ~/vagrant $ redis-server & - $ python3 app.wsgi + $ python app.py ``` 4. access +## How to run on GAE local dev server (work in progress, as we did not migrate to GAE yet) + +GAE doesn't support Redis. So you need to use Memcache instead. + +1. setup + + - Install [GAE Python SDK](https://cloud.google.com/appengine/downloads) + + +2. In config.json, + + Remove the "REDIS_INFO" section : + + ``` + "REDIS_INFO": { + ... + }, + ``` + + and add the "MEMCACHE" section like below + + ``` + "MEMCACHE": { + "servers" : [ + { + "host": "localhost", + "port": "11211" + } + ], + "debug": 0 + }, + ``` + +3. start GAE local dev server + + do not forget the last dot (.) in the command + + ``` + $ dev_appserver.py . + ``` + +4. access + + ## i18n/l10n ### Extract translatable strings from templates diff --git a/app.py b/app.py index 7b68ede..9a64c29 100644 --- a/app.py +++ b/app.py @@ -38,14 +38,16 @@ def __init__(self, file): with open(file, 'r') as f: self.cfg = json.load(f) - for section in ['OCTAV', 'REDIS_INFO', 'GITHUB', 'GOOGLE_MAP']: + for section in ['OCTAV', 'GITHUB', 'GOOGLE_MAP']: if not self.cfg.get(section): raise Exception( "missing section '" + section + "' in config file '" + file + "'" ) if self.cfg.get('OCTAV').get('BASE_URI'): raise Exception( 'DEPRECATED: {"OCTAV":{"BASE_URI"}} in config.json is deprecated.\ - Please use {"OCTAV":{"endpoint"}} instead and remove {"OCTAV":{"BASE_URI"}}.' - ) + Please use {"OCTAV":{"endpoint"}} instead and remove {"OCTAV":{"BASE_URI"}}.') + if self.cfg.get('REDIS_INFO') and self.cfg.get('MEMCACHE'): + raise Exception( 'In config.json, do not specify both "REDIS_INFO" and "MEMCACHE". Use only either of them.' ) + def section(self, name): return self.cfg.get(name) @@ -68,7 +70,12 @@ def googlemap_api_key(self): octav = Octav(**cfg.section('OCTAV')) -cache = cache.Redis(**cfg.section('REDIS_INFO')) +if cfg.section('REDIS_INFO'): + cache = cache.Redis(**cfg.section('REDIS_INFO')) +elif cfg.section('MEMCACHE'): + cache = cache.Memcache(**cfg.section('MEMCACHE')) +else: + raise Exception( 'config.json must specify either of "REDIS_INFO" or "MEMCACHE"' ) twitter = oauth.Init('twitter', base_url='https://api.twitter.com/1.1/', diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..42ad914 --- /dev/null +++ b/app.yaml @@ -0,0 +1,13 @@ +application: confweb +version: 1 +runtime: python27 +threadsafe: yes +api_version: 1 + +handlers: +- url: .* + script: app.app + +libraries: +- name: jinja2 + version: latest \ No newline at end of file diff --git a/appengine_config.py b/appengine_config.py index 56e77ad..fc28495 100644 --- a/appengine_config.py +++ b/appengine_config.py @@ -1,2 +1,12 @@ -import vendor -vendor.add('lib') \ No newline at end of file +import os +import sys + +from google.appengine.ext import vendor + +vendor.add('lib') + +# Fix for msvcrt import error https://github.com/gae-init/gae-init/pull/527 +# Otherwise, GAE local dev server fails at "import msvcrt" in "click" package +if os.name == 'nt': + os.name = None + sys.platform = '' \ No newline at end of file diff --git a/cache.py b/cache.py index 54d8c54..ccfb393 100644 --- a/cache.py +++ b/cache.py @@ -2,6 +2,11 @@ import pickle import redis +import os +if os.getenv('SERVER_SOFTWARE', '').startswith('Google App Engine/') or os.getenv('SERVER_SOFTWARE', '').startswith('Development/'): + from google.appengine.api import memcache +else: + import memcache class Redis(object): def __init__(self, host, port, db): @@ -16,3 +21,37 @@ def get(self, key): return None return pickle.loads(thing) + +class Memcache: + def __init__(self, servers=[], debug=0): + if os.getenv('SERVER_SOFTWARE', '').startswith('Google App Engine/') or os.getenv('SERVER_SOFTWARE', '').startswith('Development/'): + print "GAE memcache used" + self.client = memcache + else: + def server_str(server): + host = server.get('host') + port = server.get('port') + if not host: + raise Exception("host missing in memcache servers settings" ) + elif not port: + raise Exception("port missing in memcache servers settings" ) + else: + return str( host + ':' + port ) + + if not servers: + raise Exception("servers missing in memcache settings") + else: + server_settings = map( server_str, servers ) + print "Normal memcache used" + self.client = memcache.Client(server_settings, debug) + + def set(self, key, val, expires=0): + self.client .set(key, val, expires) + + def get(self, key): + thing = self.client .get(key) + if not thing: + return None + + return thing + diff --git a/provision.mk b/provision.mk index 0061e02..5b43b73 100644 --- a/provision.mk +++ b/provision.mk @@ -54,6 +54,7 @@ ubuntu-all: ubuntu-root: \ ubuntu-apt \ ubuntu-redis \ + ubuntu-memcached \ ubuntu-pip ubuntu-user: \ @@ -75,6 +76,9 @@ ubuntu-apt: ubuntu-redis: apt-get install -y redis-server +ubuntu-memcached: + apt-get install -y memcached + ubuntu-pip: apt-get install -y python-pip pip install -r /vagrant/requirements.txt diff --git a/requirements.txt b/requirements.txt index b4d89f4..cc61f6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ flask_oauth jinja2 py-gfm redis +python-memcached urllib3 wsgi-request-logger werkzeug diff --git a/vendor.py b/vendor.py deleted file mode 100644 index 4609019..0000000 --- a/vendor.py +++ /dev/null @@ -1,71 +0,0 @@ -# -# Copyright 2014 Jon Wayne Parrott, [proppy], Michael R. Bernstein -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Notes: -# - Imported from https://github.com/jonparrott/Darth-Vendor/. -# - Added license header. -# - Renamed `darth.vendor` to `vendor.add` to match upcoming SDK interface. -# - Renamed `position` param to `index` to match upcoming SDK interface. -# - Removed funny arworks docstring. - -import site -import os.path -import sys - - -def add(folder, index=1): - """ - Adds the given folder to the python path. Supports namespaced packages. - By default, packages in the given folder take precedence over site-packages - and any previous path manipulations. - - Args: - folder: Path to the folder containing packages, relative to ``os.getcwd()`` - position: Where in ``sys.path`` to insert the vendor packages. By default - this is set to 1. It is inadvisable to set it to 0 as it will override - any modules in the current working directory. - """ - - # Check if the path contains a virtualenv. - site_dir = os.path.join(folder, 'lib', 'python' + sys.version[:3], 'site-packages') - if os.path.exists(site_dir): - folder = site_dir - # Otherwise it's just a normal path, make it absolute. - else: - folder = os.path.join(os.path.dirname(__file__), folder) - - # Use site.addsitedir() because it appropriately reads .pth - # files for namespaced packages. Unfortunately, there's not an - # option to choose where addsitedir() puts its paths in sys.path - # so we have to do a little bit of magic to make it play along. - - # We're going to grab the current sys.path and split it up into - # the first entry and then the rest. Essentially turning - # ['.', '/site-packages/x', 'site-packages/y'] - # into - # ['.'] and ['/site-packages/x', 'site-packages/y'] - # The reason for this is we want '.' to remain at the top of the - # list but we want our vendor files to override everything else. - sys.path, remainder = sys.path[:1], sys.path[1:] - - # Now we call addsitedir which will append our vendor directories - # to sys.path (which was truncated by the last step.) - site.addsitedir(folder) - - # Finally, we'll add the paths we removed back. - # The final product is something like this: - # ['.', '/vendor-folder', /site-packages/x', 'site-packages/y'] - sys.path.extend(remainder) \ No newline at end of file