Coder Social home page Coder Social logo

muffin's Introduction

image

Muffin -- is a fast, lightweight and asyncronous ASGI web-framework for Python 3.

Tests Status

Documentation Status

PYPI Version

Python Versions


Features

Docs are available at https://klen.github.io/muffin/. Pull requests with documentation enhancements and/or fixes are awesome and most welcome.

Installation

We recommend using the latest version of Python. The library supports Python 3.8 and newer (PyPy-3.9+ are supported too).

Muffin should be installed using pip: :

pip install muffin

The command will install minimal configuration.

To install Muffin with gunicorn, uvicorn, uvloop, httptools use the command:

$ pip install muffin[standard]

Dependencies

These distributions will be installed automatically when installing Muffin.

Quickstart

Example "Hello User" with the Muffin:

import muffin


app = muffin.Application()


@app.route('/', '/hello/{name}')
async def hello(request):
    name = request.path_params.get('name', 'world')
    return f'Hello {name.title()}!'

What did that code do?

  1. First we imported the muffin.Application class. An instance of this class will be our application.
  2. Next we create an instance of this class.
  3. We then use the muffin.Application.route decorator to tell Muffin what URLs should trigger our handler function.
  4. The function returns the message we want to display in the user's browser.

Save the script as example.py and run it using Uvicorn (or another ASGI server): :

$ uvicorn example:app

Open http://localhost:8000, http://localhost:8000/hello/username in your browser. Enjoy!

Plugins overview

The list of some Muffin plugins (please make PR if you want to provide more):

Jinja2 templates (asyncio/trio/curio)

Tests Status

PYPI Version

Signed Cookie-Based HTTP sessions (asyncio/trio/curio)

Tests Status

PYPI Version

Work with OAuth (authorization, resources loading) (asyncio/trio/curio)

Tests Status

PYPI Version

Sentry integration (asyncio/trio/curio)

Tests Status

PYPI Version

Peewee support (SQL, ORM) (asyncio/trio/curio)

Tests Status

PYPI Version

Localization support (asyncio/trio/curio)

Tests Status

PYPI Version

Work with SQL databases (asyncio only)

Tests Status

PYPI Version

Work with Mongo DB (asyncio only)

Tests Status

PYPI Version

The package provides enhanced support for writing REST APIs (asyncio/trio/curio)

Tests Status

PYPI Version

Redis support

Tests Status

PYPI Version

Automatically build Admin UI

Tests Status

PYPI Version

Prometheus metrics exporter

Tests Status

PYPI Version

Benchmarks

You could find some tests here: http://klen.github.io/py-frameworks-bench/

Bug tracker

If you have any suggestions, bug reports or annoyances please report them to the issue tracker at https://github.com/klen/muffin/issues

Contributing

Development of The Muffin happens at: https://github.com/klen/muffin

Contributors

Muffin > 0.40 (completelly rewriten from scratch)

Muffin < 0.40 (based on AIOHTTP)

License

Licensed under a MIT license.

muffin's People

Contributors

abnerpc avatar danielbacci avatar dependabot[bot] avatar drgarcia1986 avatar dveselov avatar ei-grad avatar klen avatar krukov avatar marsoft avatar michaeltcoelho avatar msabramo avatar nosterx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

muffin's Issues

Question: Is muffin still active?

I see the latest commit is from Nov 13, 2017.
Currently looking for a micro-framework to pick up. Ideally I'd like to pick one that is still active.

What's the status of muffin's development?

Продвинутый вариант Class Based View (CBV)

Для тех, кто переезжает с Flask и Pyramid привычно группировать "ручки" в некоторый общий класс. Поэтому предлагаю рассмотреть вариант реализации подобного варианта в Muffin.
Подробнее в доке https://pythonhosted.org/Flask-Classy.

Кратко хочется такого:

class MyCBVHandler(muffin.Handler):
    def get(self, request):
        return {'simple': 'rest', 'example': request.match_info.get('example')}

    def post(self, request):
        return [1, 2, 3]

    @app.register('/random')
    def random(self, request):
        return choice([1, 2, 3])

NameError when providing unavailable config module

Tested on Python 3.5.1
When I specify not-existing module name as --config option or in CONFIG kw, I get the following output when starting app:

[2016-03-16 05:53:03 +0300] [9406] [INFO] Starting gunicorn 19.4.5
[2016-03-16 05:53:03 +0300] [9406] [INFO] Listening at: http://0.0.0.0:5002 (9406)
[2016-03-16 05:53:03 +0300] [9406] [INFO] Using worker: muffin.worker.GunicornWorker
[2016-03-16 05:53:03 +0300] [9449] [INFO] Booting worker with pid: 9449
ERROR:asyncio:Error in start callback
application: <Application: test>
Traceback (most recent call last):
  File "/tmp/muffin/muffin/app.py", line 188, in start
    res = cb(self, *args, **kwargs)
  File "/tmp/muffin/muffin/app.py", line 131, in <lambda>
    lambda app: app.logger.error("Error importing %s: %s", module, exc))
NameError: free variable 'exc' referenced before assignment in enclosing scope

After that error application is served.

I found that exc variable to which exception is assigned becomes unavailable when leaving except: clause, even for nested functions. When I attach that same exception to another (temporary) variable, everything works fine:

diff --git a/muffin/app.py b/muffin/app.py
index b966f0c..c106f1b 100644
--- a/muffin/app.py
+++ b/muffin/app.py
@@ -127,8 +127,9 @@ class Application(web.Application):

             except ImportError as exc:
                 config.CONFIG = None
+                exc_save = exc  # or else it will be unavailable on Python 3.5
                 self.register_on_start(
-                    lambda app: app.logger.error("Error importing %s: %s", module, exc))
+                    lambda app: app.logger.error("Error importing %s: %s", module, exc_save))

         return config

results in this output:

[2016-03-16 05:54:25 +0300] [11874] [INFO] Starting gunicorn 19.4.5
[2016-03-16 05:54:25 +0300] [11874] [INFO] Listening at: http://0.0.0.0:5002 (11874)
[2016-03-16 05:54:25 +0300] [11874] [INFO] Using worker: muffin.worker.GunicornWorker
[2016-03-16 05:54:25 +0300] [11882] [INFO] Booting worker with pid: 11882
[2016-03-16 05:54:25 +0300] [11882] [ERROR] Error importing app_cfg: No module named 'app_cfg'

For me that error message looks very strange, because I coultn't find any references to exc variable in any of enclosing scopes... May it be a bug in Python?
My patch helps to workaround this issue, but is ugly..

@app.task descriptor not working

Describe the bug
run_after_response example does not work.
AttributeError: 'Application' object has no attribute 'task'

To Reproduce
Steps to reproduce the behavior:

from muffin import Application

app = Application()

@app.task
def send_email(email, message):
# send email here
pass

@app.route('/send')
async def send(request):

# Schedule any awaitable for later execution
app.run_after_response(send_email('[email protected]', 'Hello from Muffin!'))

# Return response to a client immediately
# The task will be executed after the response is sent
return "OK"

Desktop (please complete the following information):
muffin 0.100.1

Newbie having trouble getting test client working (also trio).

I don't see anywhere in the docs where one should go for tech support. I'm trying out muffin with trio and my first test case fails with "PytestCollectionWarning: cannot collect test class 'ASGITestClient' because it has a init constructor (from: apisla/_tests/test_example.py)". I know I'm just doing something stupid but it's not apparent which stupid thing that is. Code repo is here: https://github.com/ActorForth/apisla Is there a public place I should goto for Q&A or support?

Code under test is:

import time
import datetime

#import trio
from muffin import Application, Request, ResponseJSON

app = Application()

origin_clock_start = datetime.datetime.now().timestamp() - time.perf_counter()

def pretty_time(seconds : float) -> str:
	present = time.localtime(origin_clock_start + seconds)
	return str(datetime.datetime(present.tm_year, present.tm_mon, 
			   present.tm_mday, present.tm_hour, present.tm_min, present.tm_sec))

@app.route("/")
async def timed_api(request: Request): # Callable?
	received = time.perf_counter() # trio.current_time()
	scheduled = received + 5

	print(f"Request received @ {pretty_time(received)} scheduled to reply @ {pretty_time(scheduled_response)}.")
	#trio.sleep_until(scheduled_response)
	time.sleep(5)
	handled = time.perf_counter() # trio.current_time()
	response = { 	'received': received,
					'scheduled': scheduled,
					'handled': handled,
					'delta': handled-scheduled 
				}

	return ResponseJSON(response)	

Test code:

async def test_app():
	client = TestClient(app)
	response = await client.get('/')
	assert response.status_code == 200
	assert await response.text() == 'OK'

request and response syntax

objective
can have api from cli
e.g.
request.headers['api_key']
curl -X GET -i http://localhost:8000/api/http_api_key/instrument/ -H 'api_key: stifix'
request.query_params
curl -X PUT -i "http://localhost:8000/api/put/instrument/2?name=name0&strings=0"
request.form()
curl -X PUT -d "name=name1&strings=1" -i http://localhost:8000/api/put/instrument/2
request.json()
curl -X PUT -d '{"name": "name2", "strings": 2}' -i http://localhost:8000/api/put_json/instrument/2

in starlette we can use request
e.g.

def test(request):
    #request
    request.headers['api_key']
    request.query_params
    request.form()
    request.json()

    #responses
    responses.PlainTextResponse(jwt_encode)
    responses.JSONResponse(rows)

what is equivalent of starlette request and response above in muffin?

thanks

Question: how to separate urls from views ?

Is there a standard way to do it ?

I want to separate urls from views just like Django does.
The only way I see not - creating 'urls.py' with list of lists: ['url', 'function_name']

After this importing that urls, importing all functions from views and check name correspondence, if corresponds - simulate @app.register('url_from_urls_py', function_instance).

But, maybe, there is some standard way ?

Offtop: maybe there are some ready Muffin applications for me to see usage examples ? If yes - give me a link please.

Offtop 2: if you have vk.com or Telegram account (or, maybe, there is some vk.com group for muffin), and you're not against me to ask you questions there sometimes (as rarely as possible), than give me please contacts.

Thank you.
Best regards,
Vladislav.

Up version 0.5.6 to pypi

$ pip install muffin==0.5.6
Could not find a version that satisfies the requirement muffin==0.5.6

Websocket support?

aiohttp has built in websocket handling.
Since The Muffin is built on top of aiohttp, is should do websockets as well. Is there any exposed way to use websockets with muffin?

Support for pypy3 in Ubuntu?

Muffin works fine for me with pypy3 in windows, but when I tried to host the webserver in my Ubuntu VPS, I couldn't install muffin latest version in pypy3(works fine in CPython).
I get this error:
ERROR: Could not find a version that satisfies the requirement muffin==0.83.0.
the latest version that I can install is 0.49.0.

ACCESS_LOG to syslog

from muffin options

#Logging options
'ACCESS_LOG': '-',  # File path to access log, - to stderr

Not sure the value is support only file path? or can use some class such as logging.handlers.SysLogHandler(...) ?

Do you have some example to store access_log to syslog? and is it possible if I store access_log to other server like Graylog?

Thanks,

Misleading muffin-mongo description

Muffin-Mongo -- MongoDB (pymongo) support

muffin-mongo uses asyncio-mongo, this one is a complete rewriting of a mongodb driver from scratch and so has nothing to do with pymongo.

I think this point should be clarified like this:

Muffin-Mongo -- MongoDB (asyncio-mongo) support

Не подгружается файл конфигурации по-умолчанию

Сейчас по-умолчанию muffin не подгружает никакой конфиг (по-умолчанию, это когда не установлена переменная окружения MUFFIN_CONFIG, не передан аргумент muffin --config, и не передан CONFIG при инициализации Application). Было бы неплохо если бы он подгружал config.py как написано в README.md.

webob 1.5 is broken

Pylons/webob#121

> py.test -sx --tb=native
...
========================================== FAILURES ===========================================
______________________________________ test_handler_func ______________________________________
Traceback (most recent call last):
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/response.py", line 266, in _status__set
    status_code = int(value.split()[0])
ValueError: invalid literal for int() with base 10: 'HTTP/1.0'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ei-grad/repos/muffin/tests/test_handlers.py", line 12, in test_handler_func
    response = client.get('/test')
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webtest/app.py", line 322, in get
    expect_errors=expect_errors)
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webtest/app.py", line 605, in do_request
    res = req.get_response(app, catch_exc_info=True)
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/request.py", line 1313, in send
    application, catch_exc_info=True)
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/request.py", line 1281, in call_application
    app_iter = application(self.environ, start_response)
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webtest/lint.py", line 198, in lint_app
    iterator = application(environ, start_response_wrapper)
  File "/home/ei-grad/repos/muffin/muffin/pytest.py", line 95, in handle
    res = webob.Response.from_file(handler.transport)
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/response.py", line 194, in from_file
    app_iter=(),
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/response.py", line 107, in __init__
    self.status = status
  File "/home/ei-grad/.virtualenvs/edapi-muffin/lib/python3.5/site-packages/webob/response.py", line 268, in _status__set
    raise ValueError('Invalid status code, integer required.')
ValueError: Invalid status code, integer required.
!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!
================================== 1 failed in 0.11 seconds ==================================```

Update to aiohttp>=1.2.0

Hey,

when using aiohttp.ClientSession and POSTing to a certain page I get a bug that is fixed in aiohttp==1.2.0. I'm forced to use requests for this with self-added async support. I'd like to use aiohttp.ClientSession for this like I do in all other cases. This requires aiohttp>=1.2.0, I've tested. Is there any chance for an upgrade?

There are breaking changes in aiohttp>1.0.5. I started upgrading muffin to support 1.2.0, but I was discouraged by also needing to upgrade plugins (sentry and redis) - the time required was too big to commit to it at work.

In short, changes to be made in muffin (the list may be incomplete):

  • RequestHandlerFactory became web.Server and has one parameter less
  • some classes changed places
  • from aiohttp include * in __init__.py possibly has to go away (or be updated)
  • StaticRoute doesn't exist, need to use SystemRoute (there's one other option, I don't know which one fits here)

Thank you in advance!

Proper HTTPS detection

When I server an application from behing Nginx or similar, HTTPS is not recognized.

Aiohttp has secure_proxy_ssl_header parameter which can be passed to app's make_handler method with (header, value) pair to be treated as HTTPS connection; but Muffin doesn't allow passing such pair.
I think it should be implemented in muffin.worker.GunicornWorker.make_handler() method. Probably take it somewhere from app config, with a reasonable default value.

Error: 'django_settings' when trying to execute muffin run

It seems that there's an issue on GunicornApp and i'm getting an Error: 'django_settings' error whenever i run the run command on muffin CLI.

It seems that the error is on GunicornApp.load_default_config in worker.py, It's deleting a key in a dict that it does not exist.

del self.cfg.settings['django_settings']

I'm using a docker container running a python 3.5 image upon jessie.

I'm running my app by muffin --config=guidi.dev guidi run.

Can you guys tell me why there's a key related to django in muffin?

Thanks!

Feature Request: Integration of Starlette-WTF-like Plugin for Simplified Workflow

Dear Muffin Team,

I hope this message finds you well. I am writing to express my appreciation for the excellent work you have done on Muffin, which has greatly facilitated my development process over the years. I have been consistently impressed with the framework's functionality and ease of use.

While working with Muffin, I have found that certain tasks, particularly form validation and handling, can be time-consuming and require additional boilerplate code. Recently, I came across a fantastic plugin called Starlette-WTF, specifically designed for the Starlette web framework, which has significantly simplified these tasks for me.

I would like to propose the integration of a similar plugin within Muffin, tailored to its unique features and capabilities. This plugin would enhance the framework's usability and make it even more powerful for developers. By incorporating a form validation and handling system inspired by Starlette-WTF (https://github.com/muicss/starlette-wtf), we could streamline the process of building web applications, reduce development time, and improve code readability.

Here are a few key features and benefits that I believe such a plugin could provide:

  1. Simplified form creation: The plugin should offer an intuitive way to define forms, fields, and validation rules using a concise and declarative syntax. This would eliminate the need for repetitive form code and enable developers to focus on core application logic.

  2. Automatic validation: The plugin should automatically handle form data validation, ensuring that it meets the specified criteria. By seamlessly integrating with the framework's existing request handling mechanisms, this feature would save developers from manually implementing validation logic.

  3. Error handling and feedback: The plugin should provide a robust error handling mechanism, allowing for easy identification and reporting of form validation errors. It should also facilitate the rendering of error messages within the user interface, making it straightforward to provide meaningful feedback to end-users.

  4. CSRF protection: Cross-Site Request Forgery (CSRF) protection is a crucial security concern. The plugin should incorporate CSRF protection measures, such as generating and validating tokens, to safeguard applications against malicious attacks.

  5. Integration with existing components: It would be highly beneficial if the plugin seamlessly integrates with the other components and features of [Python Framework], including routing, middleware, and templating engines. This would ensure a consistent and cohesive development experience.

Introducing a plugin inspired by Starlette-WTF would not only enhance the usability of Muffin but also attract developers who are already familiar with Starlette and appreciate its simplicity. By incorporating these features into the framework's core offering, you would empower developers to build web applications faster and more efficiently.

Thank you for considering my feature request. I believe that the integration of a Starlette-WTF-like plugin would be a valuable addition to Muffin and further solidify its position as a leading choice among Python web frameworks.

I would be more than happy to discuss this idea further or provide additional insights if needed. Please let me know if there is any further information or clarification, I can provide to support this feature request.

Thank you for your time and continued efforts in making [Python Framework] an exceptional tool for developers.

Best regards,

Kokoserver

Bug report: render_template not part of muffin?

Describe the bug
https://klen.github.io/muffin/usage.html#the-request-object has the following code

@app.route('/login', methods=['POST', 'PUT'])
 async def login(request):
     error = None
     if request.method == 'POST':
         formdata = await request.form()
         if valid_login(formdata['username'], formdata['password']):
             return log_the_user_in(formdata['username'])

         error = 'Invalid username/password'

     return render_template('login.html', error=error)

From where exactly can you can import render_template()? Or is it not part of muffin?

Py.test client fixture causes error

Добавил в тесты client-fixture.

def test_with_name(client):
    response = client.get("/test")
    assert response.status_code == 200
    assert "Hello test!" in response.text

При прогоне тестов - ошибка.

app/tests.py:16: in test_with_name
    response = client.get("/test")
/usr/lib/python3.5/site-packages/webtest/app.py:323: in get
    expect_errors=expect_errors)
/usr/lib/python3.5/site-packages/webtest/app.py:606: in do_request
    res = req.get_response(app, catch_exc_info=True)
/usr/lib/python3.5/site-packages/webob/request.py:1313: in send
    application, catch_exc_info=True)
/usr/lib/python3.5/site-packages/webob/request.py:1281: in call_application
    app_iter = application(self.environ, start_response)
/usr/lib/python3.5/site-packages/webtest/lint.py:198: in lint_app
    iterator = application(environ, start_response_wrapper)
/usr/lib/python3.5/site-packages/muffin/pytest.py:95: in handle
    loop_.run_until_complete(coro)
/usr/lib/python3.5/asyncio/base_events.py:337: in run_until_complete
    return future.result()
/usr/lib/python3.5/asyncio/futures.py:274: in result
    raise self._exception
/usr/lib/python3.5/asyncio/tasks.py:239: in _step
    result = coro.send(None)
/usr/lib/python3.5/site-packages/aiohttp/web.py:97: in handle_request
    yield from resp.write_eof()
/usr/lib/python3.5/site-packages/aiohttp/web_reqrep.py:856: in write_eof
    yield from super().write_eof()
/usr/lib/python3.5/site-packages/aiohttp/web_reqrep.py:755: in write_eof
    yield from self._resp_impl.write_eof()
/usr/lib/python3.5/asyncio/streams.py:305: in drain
    if self._transport.is_closing():
E   AttributeError: '_io.BytesIO' object has no attribute 'is_closing'

Окружение:

  • platform linux -- Python 3.5.1, pytest-2.8.7 (билд питона от 9 декабря 2015 г.)
  • alpine linux

Запускаю в контейнере Docker, но не думаю, что это влияет на что бы то ни было.

Нашел такую интеграцию webtest и aiohttp.

Но для использования этой библиотечки требуются Ваши хаки aiohttp.StreamWriter, a именно:

    monkeypatch.setattr(aiohttp.parsers.StreamWriter, 'set_tcp_cork', lambda s, v: True)
    monkeypatch.setattr(aiohttp.parsers.StreamWriter, 'set_tcp_nodelay', lambda s, v: True)

PS. Про приложение - это пример в ~10 строк с README-файла.

Inconsistent logging when importing config

There is something strange with config import problems handling.
When you specify a non-existing module, you get a message stating Error importing <modulename> (detailed since #32). When you have some imports problem within your config module, you get the same message.
At the same time, when there is some other error (like SyntaxError or whatever), it is just thrown immediately.

Also, logging actual error message is postponed to application start; but sometimes initialization of other application components depend on some config values, and will fail when config is no provided. Example:Rework Application.__init__ so that logging can be done

from muffin import Application
from redis import Redis

app = Application('test', CONFIG='no_such_config_module')
redis = Redis(app.cfg.REDIS_URL)

This example will yield AttributeError about missing REDIS_URL, without any notice that it could not find a config module. This is misleading.

So there are two improvements I would like to suggest, or alternatively one simplification:

  1. When handling ImportError in Application.cfg, check that exc.name matches module and just reraise exception otherwise. This will disable special processing when ImportError happened inside of configuration hierarchy.
  2. Change postponed logging to fire before returning from Application.__init__, not after initialization of the rest of application.
  3. Alternative way is to just abandon special processing of configuration ImportErrors, because if user provided wrong configuration value then the app is unlikely to work fine, and explicit ImportError from config loading routine looks more adequate for me than (semi-)silent ignoring such errors.

Obscurity due to sharing same config file with gunicorn

The same app config module is used both for the app itself and for Gunicorn.
But while muffin.Application only takes uppercase values, Gunicorn prefers lowercase ones.
As a result, if I set some temporary variable in the file, thinking that it will never be accessed, I can get Gunicorn reading it and using its value for something unexpected.
For example, if I have a variable named user then Gunicorn will try to switch worker processes to that user - and probably fail, if such user doesn't exist or if app is run as non-root user (which is usually the case).
The worst thing is that Gunicorn doesn't clearly define what exactly happened. In my case, I got the following output after starting app:

$ muffin app run --port 12345
[2016-07-11 15:47:06 +0000] [3] [INFO] Application is loaded: testapp
Invalid value for user: h


Error: No such user: 'h'
<env> (M=11b47) mars@bookone p36 ~/pebble/PebWare $

And that's all.
The culprit for such obscure output is gunicorn.app.base.BaseApplication.do_load_config() function which catches any exception in load_config() and prints it in an obscure way:

try:
    self.load_default_config()
    self.load_config()
except Exception as e:
    print("\nError: %s" % str(e), file=sys.stderr)
    sys.stderr.flush()
    sys.exit(1)

Probably we should request them to change this message to something more informative, or at least include a stack trace...

Also, do you think this uppercase vs lowercase difference should be documented?

Problem with global variables in interactive shell

If I use regular ipython shell, the following code works:

$ ipython --no-banner
In [1]: value = 5
In [2]: def f():
   ...:     print(value)
   ...:
In [3]: f()
5
In [4]:

But with Muffin's interactive shell it fails:

$ muffin app shell
Interactive Muffin Shell
Loaded objects: ['app']
In [1]: value = 5
In [2]: def f():
   ...:     print(value)
   ...:
In [3]: f()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-0ec059b9bfe1> in <module>()
----> 1 f()
<ipython-input-2-657c79e3b9c1> in f()
      1 def f():
----> 2     print(value)
      3
NameError: name 'value' is not defined
In [4]:

Pre-loaded objects, like app, are also not accessible from functions.
If I use muffin app shell --no-ipython then the code works (but autoindent doesn't).
Using ipython 4.0.3, python 3.5.1 and muffin 0.6.0.

Reverse url constructing broken on Python 3.5

Which is the preferred method to construct URL for given endpoint?
When I try method documented for aiohttp, it fails with an exception:

In [99]: app.router['itempage'].url()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-99-2c61d7afd253> in <module>()
----> 1 app.router['itempage'].url()

/home/mars/env/lib/python3.5/site-packages/muffin/urls.py in url(self, query, *subgroups, **groups)
     51             if k0 in groups_
     52         })
---> 53         url = ''.join(str(val) for val in Traverser(parsed, subgroups))
     54         return self._append_query(url, query)
     55

/home/mars/env/lib/python3.5/site-packages/muffin/urls.py in <genexpr>(.0)
     51             if k0 in groups_
     52         })
---> 53         url = ''.join(str(val) for val in Traverser(parsed, subgroups))
     54         return self._append_query(url, query)
     55

/home/mars/env/lib/python3.5/site-packages/muffin/urls.py in __iter__(self)
    156         """Iterate builded parts."""
    157         for state, value in self.parsed:
--> 158             yield from getattr(self, "state_" + state, self.state_default)(value)
    159
    160     def __next__(self):

TypeError: Can't convert '_NamedIntConstant' object to str implicitly

I use Python 3.5, and seems that it changed structure of re.sre_parse module (which is anyway internal) so now this code doesn't work. Does it require rewriting? Of course we could use something like str(state).lower instead of state, but it looks like a hack for me...

Graceful reload (HUP) is broken

Seems that graceful reloading on SIGHUP (which is the feature of Gunicorn) is broken in Muffin.
For now I found two problems:

  1. Muffin expects its app object to be named app and is launched as muffin <module> run. It then loads app object from the module and sets that object to Gunicorn's app. Also it passes <module> to Gunicorn as app name.
    Now, when Gunicorn tries to reload on HUP, it parses app name and tries to separate it by ':' to determine app variable name; it then cannot find a ':' and defaults variable name to application, instead of app used by muffin: https://github.com/benoitc/gunicorn/blob/master/gunicorn/util.py#L352
    My current workaround for it is as follows:
    app = Muffin(...)
    application = app
  2. It probably sets bind address in some wrong way, or maybe Gunicorn tries to load it from config somehow (I didn't find out the details yet). As a result, when I first start muffin, it binds correctly; but after killing it with HUP, it rebinds to localhost:8000 with the following log:
[2016-01-14 15:55:40 +0300] [24358] [INFO] Starting gunicorn 19.3.0
[2016-01-14 15:55:40 +0300] [24358] [INFO] Listening at: http://127.0.0.1:5000 (24358)
[2016-01-14 15:55:40 +0300] [24358] [INFO] Using worker: muffin.worker.GunicornWorker
[2016-01-14 15:55:40 +0300] [24369] [INFO] Booting worker with pid: 24369
[2016-01-14 15:55:45 +0300] [24358] [INFO] Handling signal: hup
[2016-01-14 15:55:45 +0300] [24358] [INFO] Hang up: Master
[2016-01-14 15:55:45 +0300] [24358] [INFO] Listening at: [,<,g,u,n,i,c,o,r,n,.,s,o,c,k,.,T,C,P,S,o,c,k,e,t, ,o,b,j,e,c,t, ,a,t, ,0,x,7,f,1,d,9,8,b,f,1,1,6,0,>,]
[2016-01-14 15:55:45 +0300] [24477] [INFO] Booting worker with pid: 24477
[2016-01-14 15:55:45 +0300] [24369] [INFO] Stopping server: 24369, connections: 0
[2016-01-14 15:55:45 +0300] [24369] [INFO] Worker exiting (pid: 24369)

Line with [,<,g,u,n,i,... is result of a bug in Gunicorn - see benoitc/gunicorn#1181; actually it is a http://127.0.0.1:8000, which takes its origin from https://github.com/benoitc/gunicorn/blob/master/gunicorn/config.py#L498

For now I yet have no ideas on how to fix these problems.

Reload doesn't work

I have enabled code reloading (with either DEBUG=True or --reload option). Now whenever I change any code, worker is getting reloaded, but it is serving old code.
I encountered such behaviour before with Flask when Gunicorn was start with --preload option which is incompatible with --reload.

An option to change static files headers?

Muffin serves the files with the header "content-disposition: attachment; filename="..." and that makes the browser download the files instead of displaying them.

Getting timeout logs every second when DEBUG=True

Приветствую.
При запущенном приложении с опцией DEBUG=True почти каждую секунду получаю в логах по одной строчке такого вида: INFO:asyncio:poll 999.495 ms took 1000.397 ms: timeout (см. ниже аттач).
Насколько я понял, логируется это где-то в недрах asyncio, но вот чем оно вызвано и как побороть - неясно. С выключенным DEBUG этих сообщений, само собой, не видно. Происходит даже с пустой аппой без каких-либо плагинов.
Посоветуйте, в каком направлении следует копать?

Пока нашёл соответствующий кусок кода из питоньих исходников: https://github.com/python/cpython/blob/3.5/Lib/asyncio/base_events.py#L1204

$ muffin main run
[2015-10-28 05:56:33 +0300] [2553] [INFO] Starting gunicorn 19.3.0
[2015-10-28 05:56:33 +0300] [2553] [INFO] Listening at: http://127.0.0.1:5000 (2553)
[2015-10-28 05:56:33 +0300] [2553] [INFO] Using worker: muffin.worker.GunicornWorker
[2015-10-28 05:56:33 +0300] [2565] [INFO] Booting worker with pid: 2565
INFO:asyncio:<Server sockets=[<socket.socket fd=8, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('127.0.0.1', 5000)>]> is serving
INFO:asyncio:poll 999.717 ms took 1003.139 ms: timeout
INFO:asyncio:poll 999.137 ms took 1000.672 ms: timeout
INFO:asyncio:poll 999.259 ms took 1000.242 ms: timeout
INFO:asyncio:poll 999.156 ms took 1001.158 ms: timeout
INFO:asyncio:poll 999.094 ms took 1001.137 ms: timeout
INFO:asyncio:poll 999.574 ms took 1001.132 ms: timeout
INFO:asyncio:poll 999.408 ms took 1001.087 ms: timeout
INFO:asyncio:poll 999.502 ms took 1001.118 ms: timeout
INFO:asyncio:poll 999.502 ms took 1000.858 ms: timeout
INFO:asyncio:poll 998.919 ms took 1000.138 ms: timeout
INFO:asyncio:poll 999.406 ms took 1001.078 ms: timeout
INFO:asyncio:poll 999.495 ms took 1000.397 ms: timeout

Система (в virtualenv):

Python 3.5.0
aiohttp==0.17.4
Babel==2.1.1
cached-property==1.2.0
chardet==2.3.0
decorator==4.0.4
gunicorn==19.3.0
ipython==4.0.0
ipython-genutils==0.1.0
Jinja2==2.7.3
MarkupSafe==0.23
muffin==0.2.1
muffin-admin==0.1.3
muffin-babel==0.0.6
muffin-debugtoolbar==0.1.2
muffin-jinja2==0.1.0
muffin-peewee==0.4.0
muffin-session==0.0.8
path.py==8.1.2
peewee==2.6.4
pexpect==4.0.1
pickleshare==0.5
ptyprocess==0.5
PyMySQL==0.6.7
pytz==2015.7
simplegeneric==0.8.1
speaklater==1.3
traitlets==4.0.0
ujson==1.33
wheel==0.24.0
WTForms==2.0.2

PS. Прошу прощения за простыню, аттач гитхаб не позволяет сделать.

Muffin handlers hang when using a Rethinkdb connecton that was made outside of Muffin's loop

For some reason Muffin handlers hang when using a Rethinkdb connecton that was made outside of Muffin's loop. I realise that it isn't necessarily Muffin's fault, but please take a look at the very simple examples below.

The following script works as expected (returns "1+1 = 2" on each page load):

import muffin
import rethinkdb as r

r.set_loop_type('asyncio')

app = muffin.Application('example')


@app.register('/')
async def handle(request):
    db = await r.connect()  # <--- Connect every time
    js = '1+1'
    res = await r.js(js).run(db)
    text = "%s = %s" % (js, res)
    return text


if __name__ == '__main__':
    app.manage()

The following script hangs on page load (and then it dies in long agony on ^C rather than simply quits):

import muffin
import asyncio
import rethinkdb as r

r.set_loop_type('asyncio')

db = asyncio.get_event_loop().run_until_complete(r.connect())  # <--- Connect only once

app = muffin.Application('example')


@app.register('/')
async def handle(request):
    js = '1+1'
    res = await r.js(js).run(db)
    text = "%s = %s" % (js, res)
    return text


if __name__ == '__main__':
    app.manage()

I don't think it's purely Rethinkdb problem because the same approach works with aiohttp:

from aiohttp import web
import asyncio
import rethinkdb as r

r.set_loop_type('asyncio')

db = asyncio.get_event_loop().run_until_complete(r.connect())  # <--- Connect only once


async def handle(request):
    js = '1+1'
    res = await r.js(js).run(db)
    text = "%s = %s" % (js, res)
    return web.Response(body=text.encode('utf-8'))


app = web.Application()
app.router.add_route('GET', '/', handle)

web.run_app(app)

Зачем нужен LStruct?

Кажется что запрет на изменение конфига в runtime это лишнее. Это вряд ли предотвратит большое количество ошибок и выглядит не по питонячьи.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.