diff --git a/pyramid_cookie_sql/.coveragerc b/pyramid_cookie_sql/.coveragerc new file mode 100644 index 0000000..d8aee03 --- /dev/null +++ b/pyramid_cookie_sql/.coveragerc @@ -0,0 +1,3 @@ +[run] +source = cookies +omit = cookies/test* diff --git a/pyramid_cookie_sql/.gitignore b/pyramid_cookie_sql/.gitignore new file mode 100644 index 0000000..1853d98 --- /dev/null +++ b/pyramid_cookie_sql/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/pyramid_cookie_sql/CHANGES.txt b/pyramid_cookie_sql/CHANGES.txt new file mode 100644 index 0000000..14b902f --- /dev/null +++ b/pyramid_cookie_sql/CHANGES.txt @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version. diff --git a/pyramid_cookie_sql/MANIFEST.in b/pyramid_cookie_sql/MANIFEST.in new file mode 100644 index 0000000..b64c3d0 --- /dev/null +++ b/pyramid_cookie_sql/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include cookies *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2 diff --git a/pyramid_cookie_sql/README.txt b/pyramid_cookie_sql/README.txt new file mode 100644 index 0000000..062815c --- /dev/null +++ b/pyramid_cookie_sql/README.txt @@ -0,0 +1,43 @@ +cookies +======= + +Getting Started +--------------- + +- Change directory into your newly created project. + + cd cookies + +- Create a Python virtual environment. + + python3 -m venv env + +- Upgrade packaging tools. + + env/bin/pip install --upgrade pip setuptools + +- Install the project in editable mode with its testing requirements. + + env/bin/pip install -e ".[testing]" + +- Initialize and upgrade the database using Alembic. + + - Generate your first revision. + + env/bin/alembic -c development.ini revision --autogenerate -m "init" + + - Upgrade to that revision. + + env/bin/alembic -c development.ini upgrade head + +- Load default data into the database using a script. + + env/bin/initialize_cookies_db development.ini + +- Run your project's tests. + + env/bin/pytest + +- Run your project. + + env/bin/pserve development.ini diff --git a/pyramid_cookie_sql/cookies/__init__.py b/pyramid_cookie_sql/cookies/__init__.py new file mode 100644 index 0000000..5c2ba5c --- /dev/null +++ b/pyramid_cookie_sql/cookies/__init__.py @@ -0,0 +1,12 @@ +from pyramid.config import Configurator + + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + with Configurator(settings=settings) as config: + config.include('.models') + config.include('pyramid_jinja2') + config.include('.routes') + config.scan() + return config.make_wsgi_app() diff --git a/pyramid_cookie_sql/cookies/alembic/env.py b/pyramid_cookie_sql/cookies/alembic/env.py new file mode 100644 index 0000000..48b7221 --- /dev/null +++ b/pyramid_cookie_sql/cookies/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from cookies.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/pyramid_cookie_sql/cookies/alembic/script.py.mako b/pyramid_cookie_sql/cookies/alembic/script.py.mako new file mode 100644 index 0000000..535780d --- /dev/null +++ b/pyramid_cookie_sql/cookies/alembic/script.py.mako @@ -0,0 +1,22 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + +def upgrade(): + ${upgrades if upgrades else "pass"} + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/pyramid_cookie_sql/cookies/alembic/versions/README.txt b/pyramid_cookie_sql/cookies/alembic/versions/README.txt new file mode 100644 index 0000000..b0d704d --- /dev/null +++ b/pyramid_cookie_sql/cookies/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions diff --git a/pyramid_cookie_sql/cookies/models/__init__.py b/pyramid_cookie_sql/cookies/models/__init__.py new file mode 100644 index 0000000..863a42d --- /dev/null +++ b/pyramid_cookie_sql/cookies/models/__init__.py @@ -0,0 +1,77 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import configure_mappers +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines +from .mymodel import MyModel # flake8: noqa + +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup +configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('cookies.models')``. + + """ + settings = config.get_settings() + settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + # use pyramid_retry to retry a request when transient exceptions occur + config.include('pyramid_retry') + + session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/pyramid_cookie_sql/cookies/models/meta.py b/pyramid_cookie_sql/cookies/models/meta.py new file mode 100644 index 0000000..02285b3 --- /dev/null +++ b/pyramid_cookie_sql/cookies/models/meta.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.schema import MetaData + +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html +NAMING_CONVENTION = { + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s" +} + +metadata = MetaData(naming_convention=NAMING_CONVENTION) +Base = declarative_base(metadata=metadata) diff --git a/pyramid_cookie_sql/cookies/models/mymodel.py b/pyramid_cookie_sql/cookies/models/mymodel.py new file mode 100644 index 0000000..d65a01a --- /dev/null +++ b/pyramid_cookie_sql/cookies/models/mymodel.py @@ -0,0 +1,18 @@ +from sqlalchemy import ( + Column, + Index, + Integer, + Text, +) + +from .meta import Base + + +class MyModel(Base): + __tablename__ = 'models' + id = Column(Integer, primary_key=True) + name = Column(Text) + value = Column(Integer) + + +Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/pyramid_cookie_sql/cookies/pshell.py b/pyramid_cookie_sql/cookies/pshell.py new file mode 100644 index 0000000..b0847ee --- /dev/null +++ b/pyramid_cookie_sql/cookies/pshell.py @@ -0,0 +1,13 @@ +from . import models + + +def setup(env): + request = env['request'] + + # start a transaction + request.tm.begin() + + # inject some vars into the shell builtins + env['tm'] = request.tm + env['dbsession'] = request.dbsession + env['models'] = models diff --git a/pyramid_cookie_sql/cookies/routes.py b/pyramid_cookie_sql/cookies/routes.py new file mode 100644 index 0000000..25504ad --- /dev/null +++ b/pyramid_cookie_sql/cookies/routes.py @@ -0,0 +1,3 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') diff --git a/pyramid_cookie_sql/cookies/scripts/__init__.py b/pyramid_cookie_sql/cookies/scripts/__init__.py new file mode 100644 index 0000000..5bb534f --- /dev/null +++ b/pyramid_cookie_sql/cookies/scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid_cookie_sql/cookies/scripts/initialize_db.py b/pyramid_cookie_sql/cookies/scripts/initialize_db.py new file mode 100644 index 0000000..c629d17 --- /dev/null +++ b/pyramid_cookie_sql/cookies/scripts/initialize_db.py @@ -0,0 +1,48 @@ +import argparse +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + """ + Add or update models / fixtures in the database. + + """ + model = models.mymodel.MyModel(name='one', value=1) + dbsession.add(model) + + +def parse_args(argv): + parser = argparse.ArgumentParser() + parser.add_argument( + 'config_uri', + help='Configuration file, e.g., development.ini', + ) + return parser.parse_args(argv[1:]) + + +def main(argv=sys.argv): + args = parse_args(argv) + setup_logging(args.config_uri) + env = bootstrap(args.config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/pyramid_cookie_sql/cookies/static/pyramid-16x16.png b/pyramid_cookie_sql/cookies/static/pyramid-16x16.png new file mode 100644 index 0000000..9792031 Binary files /dev/null and b/pyramid_cookie_sql/cookies/static/pyramid-16x16.png differ diff --git a/pyramid_cookie_sql/cookies/static/pyramid.png b/pyramid_cookie_sql/cookies/static/pyramid.png new file mode 100644 index 0000000..4ab837b Binary files /dev/null and b/pyramid_cookie_sql/cookies/static/pyramid.png differ diff --git a/pyramid_cookie_sql/cookies/static/theme.css b/pyramid_cookie_sql/cookies/static/theme.css new file mode 100644 index 0000000..0f4b1a4 --- /dev/null +++ b/pyramid_cookie_sql/cookies/static/theme.css @@ -0,0 +1,154 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700); +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #ffffff; + background: #bc2131; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +p { + font-weight: 300; +} +.font-normal { + font-weight: 400; +} +.font-semi-bold { + font-weight: 600; +} +.font-bold { + font-weight: 700; +} +.starter-template { + margin-top: 250px; +} +.starter-template .content { + margin-left: 10px; +} +.starter-template .content h1 { + margin-top: 10px; + font-size: 60px; +} +.starter-template .content h1 .smaller { + font-size: 40px; + color: #f2b7bd; +} +.starter-template .content .lead { + font-size: 25px; + color: #f2b7bd; +} +.starter-template .content .lead .font-normal { + color: #ffffff; +} +.starter-template .links { + float: right; + right: 0; + margin-top: 125px; +} +.starter-template .links ul { + display: block; + padding: 0; + margin: 0; +} +.starter-template .links ul li { + list-style: none; + display: inline; + margin: 0 10px; +} +.starter-template .links ul li:first-child { + margin-left: 0; +} +.starter-template .links ul li:last-child { + margin-right: 0; +} +.starter-template .links ul li.current-version { + color: #f2b7bd; + font-weight: 400; +} +.starter-template .links ul li a, a { + color: #f2b7bd; + text-decoration: underline; +} +.starter-template .links ul li a:hover, a:hover { + color: #ffffff; + text-decoration: underline; +} +.starter-template .links ul li .icon-muted { + color: #eb8b95; + margin-right: 5px; +} +.starter-template .links ul li:hover .icon-muted { + color: #ffffff; +} +.starter-template .copyright { + margin-top: 10px; + font-size: 0.9em; + color: #f2b7bd; + text-transform: lowercase; + float: right; + right: 0; +} +@media (max-width: 1199px) { + .starter-template .content h1 { + font-size: 45px; + } + .starter-template .content h1 .smaller { + font-size: 30px; + } + .starter-template .content .lead { + font-size: 20px; + } +} +@media (max-width: 991px) { + .starter-template { + margin-top: 0; + } + .starter-template .logo { + margin: 40px auto; + } + .starter-template .content { + margin-left: 0; + text-align: center; + } + .starter-template .content h1 { + margin-bottom: 20px; + } + .starter-template .links { + float: none; + text-align: center; + margin-top: 60px; + } + .starter-template .copyright { + float: none; + text-align: center; + } +} +@media (max-width: 767px) { + .starter-template .content h1 .smaller { + font-size: 25px; + display: block; + } + .starter-template .content .lead { + font-size: 16px; + } + .starter-template .links { + margin-top: 40px; + } + .starter-template .links ul li { + display: block; + margin: 0; + } + .starter-template .links ul li .icon-muted { + display: none; + } + .starter-template .copyright { + margin-top: 20px; + } +} diff --git a/pyramid_cookie_sql/cookies/templates/404.jinja2 b/pyramid_cookie_sql/cookies/templates/404.jinja2 new file mode 100644 index 0000000..aaf1241 --- /dev/null +++ b/pyramid_cookie_sql/cookies/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Starter project

+

404 Page Not Found

+
+{% endblock content %} diff --git a/pyramid_cookie_sql/cookies/templates/layout.jinja2 b/pyramid_cookie_sql/cookies/templates/layout.jinja2 new file mode 100644 index 0000000..23aad57 --- /dev/null +++ b/pyramid_cookie_sql/cookies/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + + + + + + + + + + Cookiecutter Starter project for the Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+ +
+ +
+
+
+ + + + + + + + diff --git a/pyramid_cookie_sql/cookies/templates/mytemplate.jinja2 b/pyramid_cookie_sql/cookies/templates/mytemplate.jinja2 new file mode 100644 index 0000000..f2e7283 --- /dev/null +++ b/pyramid_cookie_sql/cookies/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Starter project

+

Welcome to {{project}}, a Pyramid application generated by
Cookiecutter.

+
+{% endblock content %} diff --git a/pyramid_cookie_sql/cookies/tests.py b/pyramid_cookie_sql/cookies/tests.py new file mode 100644 index 0000000..5acc108 --- /dev/null +++ b/pyramid_cookie_sql/cookies/tests.py @@ -0,0 +1,66 @@ +import unittest + +from pyramid import testing + +import transaction + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models') + settings = self.config.get_settings() + + from .models import ( + get_engine, + get_session_factory, + get_tm_session, + ) + + self.engine = get_engine(settings) + session_factory = get_session_factory(self.engine) + + self.session = get_tm_session(session_factory, transaction.manager) + + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) + + def tearDown(self): + from .models.meta import Base + + testing.tearDown() + transaction.abort() + Base.metadata.drop_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): + + def setUp(self): + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() + + from .models import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'cookies') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/pyramid_cookie_sql/cookies/views/__init__.py b/pyramid_cookie_sql/cookies/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyramid_cookie_sql/cookies/views/default.py b/pyramid_cookie_sql/cookies/views/default.py new file mode 100644 index 0000000..a6c0ec6 --- /dev/null +++ b/pyramid_cookie_sql/cookies/views/default.py @@ -0,0 +1,32 @@ +from pyramid.view import view_config +from pyramid.response import Response + +from sqlalchemy.exc import DBAPIError + +from .. import models + + +@view_config(route_name='home', renderer='../templates/mytemplate.jinja2') +def my_view(request): + try: + query = request.dbsession.query(models.MyModel) + one = query.filter(models.MyModel.name == 'one').first() + except DBAPIError: + return Response(db_err_msg, content_type='text/plain', status=500) + return {'one': one, 'project': 'cookies'} + + +db_err_msg = """\ +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for descriptions and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + +After you fix the problem, please restart the Pyramid application to +try it again. +""" diff --git a/pyramid_cookie_sql/cookies/views/notfound.py b/pyramid_cookie_sql/cookies/views/notfound.py new file mode 100644 index 0000000..69d6e28 --- /dev/null +++ b/pyramid_cookie_sql/cookies/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/pyramid_cookie_sql/development.ini b/pyramid_cookie_sql/development.ini new file mode 100644 index 0000000..954a7a0 --- /dev/null +++ b/pyramid_cookie_sql/development.ini @@ -0,0 +1,80 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cookies + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + +sqlalchemy.url = sqlite:///%(here)s/cookies.sqlite + +retry.attempts = 3 + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +[pshell] +setup = cookies.pshell.setup + +### +# wsgi server configuration +### + +[alembic] +# path to migration scripts +script_location = cookies/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s +# file_template = %%(rev)s_%%(slug)s + +[server:main] +use = egg:waitress#main +listen = localhost:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cookies, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_cookies] +level = DEBUG +handlers = +qualname = cookies + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/pyramid_cookie_sql/production.ini b/pyramid_cookie_sql/production.ini new file mode 100644 index 0000000..fc2eb61 --- /dev/null +++ b/pyramid_cookie_sql/production.ini @@ -0,0 +1,74 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cookies + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +sqlalchemy.url = sqlite:///%(here)s/cookies.sqlite + +retry.attempts = 3 + +[pshell] +setup = cookies.pshell.setup + +### +# wsgi server configuration +### + +[alembic] +# path to migration scripts +script_location = cookies/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s +# file_template = %%(rev)s_%%(slug)s + +[server:main] +use = egg:waitress#main +listen = *:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cookies, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_cookies] +level = WARN +handlers = +qualname = cookies + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/pyramid_cookie_sql/pytest.ini b/pyramid_cookie_sql/pytest.ini new file mode 100644 index 0000000..b48d1f9 --- /dev/null +++ b/pyramid_cookie_sql/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +testpaths = cookies +python_files = test*.py diff --git a/pyramid_cookie_sql/setup.py b/pyramid_cookie_sql/setup.py new file mode 100644 index 0000000..bc82a9d --- /dev/null +++ b/pyramid_cookie_sql/setup.py @@ -0,0 +1,61 @@ +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.txt')) as f: + README = f.read() +with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + +requires = [ + 'plaster_pastedeploy', + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'waitress', + 'alembic', + 'pyramid_retry', + 'pyramid_tm', + 'SQLAlchemy', + 'transaction', + 'zope.sqlalchemy', +] + +tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest >= 3.7.4', + 'pytest-cov', +] + +setup( + name='cookies', + version='0.0', + description='cookies', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + extras_require={ + 'testing': tests_require, + }, + install_requires=requires, + entry_points={ + 'paste.app_factory': [ + 'main = cookies:main', + ], + 'console_scripts': [ + 'initialize_cookies_db=cookies.scripts.initialize_db:main', + ], + }, +) diff --git a/pyramid_cookie_trav/.coveragerc b/pyramid_cookie_trav/.coveragerc new file mode 100644 index 0000000..0a56c42 --- /dev/null +++ b/pyramid_cookie_trav/.coveragerc @@ -0,0 +1,3 @@ +[run] +source = cookie +omit = cookie/test* diff --git a/pyramid_cookie_trav/.gitignore b/pyramid_cookie_trav/.gitignore new file mode 100644 index 0000000..1853d98 --- /dev/null +++ b/pyramid_cookie_trav/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/pyramid_cookie_trav/CHANGES.txt b/pyramid_cookie_trav/CHANGES.txt new file mode 100644 index 0000000..14b902f --- /dev/null +++ b/pyramid_cookie_trav/CHANGES.txt @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version. diff --git a/pyramid_cookie_trav/MANIFEST.in b/pyramid_cookie_trav/MANIFEST.in new file mode 100644 index 0000000..6f8c57c --- /dev/null +++ b/pyramid_cookie_trav/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include cookie *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2 diff --git a/pyramid_cookie_trav/README.txt b/pyramid_cookie_trav/README.txt new file mode 100644 index 0000000..8d3fc34 --- /dev/null +++ b/pyramid_cookie_trav/README.txt @@ -0,0 +1,29 @@ +cookie +====== + +Getting Started +--------------- + +- Change directory into your newly created project. + + cd cookie + +- Create a Python virtual environment. + + python3 -m venv env + +- Upgrade packaging tools. + + env/bin/pip install --upgrade pip setuptools + +- Install the project in editable mode with its testing requirements. + + env/bin/pip install -e ".[testing]" + +- Run your project's tests. + + env/bin/pytest + +- Run your project. + + env/bin/pserve development.ini diff --git a/pyramid_cookie_trav/cookie/__init__.py b/pyramid_cookie_trav/cookie/__init__.py new file mode 100644 index 0000000..badb31b --- /dev/null +++ b/pyramid_cookie_trav/cookie/__init__.py @@ -0,0 +1,23 @@ +from pyramid.config import Configurator +from pyramid_zodbconn import get_connection +from .models import appmaker + + +def root_factory(request): + conn = get_connection(request) + return appmaker(conn.root()) + + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + with Configurator(settings=settings) as config: + settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' + config.include('pyramid_tm') + config.include('pyramid_retry') + config.include('pyramid_zodbconn') + config.set_root_factory(root_factory) + config.include('pyramid_jinja2') + config.include('.routes') + config.scan() + return config.make_wsgi_app() diff --git a/pyramid_cookie_trav/cookie/models/__init__.py b/pyramid_cookie_trav/cookie/models/__init__.py new file mode 100644 index 0000000..aca6a41 --- /dev/null +++ b/pyramid_cookie_trav/cookie/models/__init__.py @@ -0,0 +1,12 @@ +from persistent.mapping import PersistentMapping + + +class MyModel(PersistentMapping): + __parent__ = __name__ = None + + +def appmaker(zodb_root): + if 'app_root' not in zodb_root: + app_root = MyModel() + zodb_root['app_root'] = app_root + return zodb_root['app_root'] diff --git a/pyramid_cookie_trav/cookie/pshell.py b/pyramid_cookie_trav/cookie/pshell.py new file mode 100644 index 0000000..a7cfa6a --- /dev/null +++ b/pyramid_cookie_trav/cookie/pshell.py @@ -0,0 +1,12 @@ +from . import models + + +def setup(env): + request = env['request'] + + # start a transaction + request.tm.begin() + + # inject some vars into the shell builtins + env['tm'] = request.tm + env['models'] = models diff --git a/pyramid_cookie_trav/cookie/routes.py b/pyramid_cookie_trav/cookie/routes.py new file mode 100644 index 0000000..3c0a379 --- /dev/null +++ b/pyramid_cookie_trav/cookie/routes.py @@ -0,0 +1,2 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) diff --git a/pyramid_cookie_trav/cookie/static/pyramid-16x16.png b/pyramid_cookie_trav/cookie/static/pyramid-16x16.png new file mode 100644 index 0000000..9792031 Binary files /dev/null and b/pyramid_cookie_trav/cookie/static/pyramid-16x16.png differ diff --git a/pyramid_cookie_trav/cookie/static/pyramid.png b/pyramid_cookie_trav/cookie/static/pyramid.png new file mode 100644 index 0000000..4ab837b Binary files /dev/null and b/pyramid_cookie_trav/cookie/static/pyramid.png differ diff --git a/pyramid_cookie_trav/cookie/static/theme.css b/pyramid_cookie_trav/cookie/static/theme.css new file mode 100644 index 0000000..0f4b1a4 --- /dev/null +++ b/pyramid_cookie_trav/cookie/static/theme.css @@ -0,0 +1,154 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700); +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #ffffff; + background: #bc2131; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +p { + font-weight: 300; +} +.font-normal { + font-weight: 400; +} +.font-semi-bold { + font-weight: 600; +} +.font-bold { + font-weight: 700; +} +.starter-template { + margin-top: 250px; +} +.starter-template .content { + margin-left: 10px; +} +.starter-template .content h1 { + margin-top: 10px; + font-size: 60px; +} +.starter-template .content h1 .smaller { + font-size: 40px; + color: #f2b7bd; +} +.starter-template .content .lead { + font-size: 25px; + color: #f2b7bd; +} +.starter-template .content .lead .font-normal { + color: #ffffff; +} +.starter-template .links { + float: right; + right: 0; + margin-top: 125px; +} +.starter-template .links ul { + display: block; + padding: 0; + margin: 0; +} +.starter-template .links ul li { + list-style: none; + display: inline; + margin: 0 10px; +} +.starter-template .links ul li:first-child { + margin-left: 0; +} +.starter-template .links ul li:last-child { + margin-right: 0; +} +.starter-template .links ul li.current-version { + color: #f2b7bd; + font-weight: 400; +} +.starter-template .links ul li a, a { + color: #f2b7bd; + text-decoration: underline; +} +.starter-template .links ul li a:hover, a:hover { + color: #ffffff; + text-decoration: underline; +} +.starter-template .links ul li .icon-muted { + color: #eb8b95; + margin-right: 5px; +} +.starter-template .links ul li:hover .icon-muted { + color: #ffffff; +} +.starter-template .copyright { + margin-top: 10px; + font-size: 0.9em; + color: #f2b7bd; + text-transform: lowercase; + float: right; + right: 0; +} +@media (max-width: 1199px) { + .starter-template .content h1 { + font-size: 45px; + } + .starter-template .content h1 .smaller { + font-size: 30px; + } + .starter-template .content .lead { + font-size: 20px; + } +} +@media (max-width: 991px) { + .starter-template { + margin-top: 0; + } + .starter-template .logo { + margin: 40px auto; + } + .starter-template .content { + margin-left: 0; + text-align: center; + } + .starter-template .content h1 { + margin-bottom: 20px; + } + .starter-template .links { + float: none; + text-align: center; + margin-top: 60px; + } + .starter-template .copyright { + float: none; + text-align: center; + } +} +@media (max-width: 767px) { + .starter-template .content h1 .smaller { + font-size: 25px; + display: block; + } + .starter-template .content .lead { + font-size: 16px; + } + .starter-template .links { + margin-top: 40px; + } + .starter-template .links ul li { + display: block; + margin: 0; + } + .starter-template .links ul li .icon-muted { + display: none; + } + .starter-template .copyright { + margin-top: 20px; + } +} diff --git a/pyramid_cookie_trav/cookie/templates/404.jinja2 b/pyramid_cookie_trav/cookie/templates/404.jinja2 new file mode 100644 index 0000000..aaf1241 --- /dev/null +++ b/pyramid_cookie_trav/cookie/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Starter project

+

404 Page Not Found

+
+{% endblock content %} diff --git a/pyramid_cookie_trav/cookie/templates/layout.jinja2 b/pyramid_cookie_trav/cookie/templates/layout.jinja2 new file mode 100644 index 0000000..895ca46 --- /dev/null +++ b/pyramid_cookie_trav/cookie/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + + + + + + + + + + Cookiecutter Starter project for the Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+ +
+ +
+
+
+ + + + + + + + diff --git a/pyramid_cookie_trav/cookie/templates/mytemplate.jinja2 b/pyramid_cookie_trav/cookie/templates/mytemplate.jinja2 new file mode 100644 index 0000000..f2e7283 --- /dev/null +++ b/pyramid_cookie_trav/cookie/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Starter project

+

Welcome to {{project}}, a Pyramid application generated by
Cookiecutter.

+
+{% endblock content %} diff --git a/pyramid_cookie_trav/cookie/tests.py b/pyramid_cookie_trav/cookie/tests.py new file mode 100644 index 0000000..67892b1 --- /dev/null +++ b/pyramid_cookie_trav/cookie/tests.py @@ -0,0 +1,18 @@ +import unittest + +from pyramid import testing + + +class ViewTests(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_my_view(self): + from .views.default import my_view + request = testing.DummyRequest() + info = my_view(request) + self.assertEqual(info['project'], 'cookie') + diff --git a/pyramid_cookie_trav/cookie/views/__init__.py b/pyramid_cookie_trav/cookie/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyramid_cookie_trav/cookie/views/default.py b/pyramid_cookie_trav/cookie/views/default.py new file mode 100644 index 0000000..1b9c653 --- /dev/null +++ b/pyramid_cookie_trav/cookie/views/default.py @@ -0,0 +1,8 @@ +from pyramid.view import view_config + +from ..models import MyModel + + +@view_config(context=MyModel, renderer='../templates/mytemplate.jinja2') +def my_view(request): + return {'project': 'cookie'} diff --git a/pyramid_cookie_trav/cookie/views/notfound.py b/pyramid_cookie_trav/cookie/views/notfound.py new file mode 100644 index 0000000..69d6e28 --- /dev/null +++ b/pyramid_cookie_trav/cookie/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/pyramid_cookie_trav/development.ini b/pyramid_cookie_trav/development.ini new file mode 100644 index 0000000..a97018c --- /dev/null +++ b/pyramid_cookie_trav/development.ini @@ -0,0 +1,66 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cookie + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + +zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 + +retry.attempts = 3 + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +[pshell] +setup = cookie.pshell.setup + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +listen = localhost:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cookie + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_cookie] +level = DEBUG +handlers = +qualname = cookie + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/pyramid_cookie_trav/production.ini b/pyramid_cookie_trav/production.ini new file mode 100644 index 0000000..535c7df --- /dev/null +++ b/pyramid_cookie_trav/production.ini @@ -0,0 +1,60 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cookie + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 + +retry.attempts = 3 + +[pshell] +setup = cookie.pshell.setup + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +listen = *:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cookie + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_cookie] +level = WARN +handlers = +qualname = cookie + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/pyramid_cookie_trav/pytest.ini b/pyramid_cookie_trav/pytest.ini new file mode 100644 index 0000000..9116b4c --- /dev/null +++ b/pyramid_cookie_trav/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +testpaths = cookie +python_files = test*.py diff --git a/pyramid_cookie_trav/setup.py b/pyramid_cookie_trav/setup.py new file mode 100644 index 0000000..57e1fab --- /dev/null +++ b/pyramid_cookie_trav/setup.py @@ -0,0 +1,57 @@ +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.txt')) as f: + README = f.read() +with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + +requires = [ + 'plaster_pastedeploy', + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'waitress', + 'pyramid_retry', + 'pyramid_tm', + 'pyramid_zodbconn', + 'transaction', + 'ZODB3', +] + +tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest >= 3.7.4', + 'pytest-cov', +] + +setup( + name='cookie', + version='0.0', + description='cookie', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + extras_require={ + 'testing': tests_require, + }, + install_requires=requires, + entry_points={ + 'paste.app_factory': [ + 'main = cookie:main', + ], + }, +)