среда, 12 июня 2024 г.

Pydantic (or Django Ninja) + Timeflake

 So if you want to publish some model to your rest api, and model is declared using Pydantic or Django Ninja (which is using Pydantic under the hood), here is the solution I came with:

from typing import Any, Annotated

from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler, TypeAdapter
from pydantic.types import AnyType
from pydantic_core import CoreSchema, core_schema
from pydantic.json_schema import JsonSchemaValue


def vld(x: Any) -> Any:
return x

class TimeflakeType:

@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
serializer = core_schema.plain_serializer_function_ser_schema(cls._serialize, when_used='json')
if cls is source:
return core_schema.no_info_plain_validator_function(
function=vld, serialization=serializer
)
else:
return core_schema.no_info_before_validator_function(
function=vld, schema=handler(source), serialization=serializer
)
@classmethod
def __get_pydantic_json_schema__(cls, cs: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue:
return handler(core_schema.str_schema())

@staticmethod
def _serialize(v: Any) -> str:
return str(v)

def __repr__(self) -> str:
return 'Timeflake'

This code is based on ImportString type declaration in standard Pydantic repo. 

воскресенье, 7 января 2018 г.

Getting started with N2O Erlang web framework

Synrc.com guys did a great job with creating N2O framework. Let's try to make some app with it.
But before we start we need to install MAD. Mad is a deps management & build kind of tool.
Let's download it. Open this link and press Download button.  What we've just got is a file with compiled erlang bytecode inside and it's self executable.
Place it somewere in your $PATH (for example "/usr/local/bin") just to be able to execute it anywhere in your console.
Then you can type "mad app helloworld" which is going to create a new directory called helloworld with a bunch of files in it.
helloworld/rebar.config will have something like this in it:
{sub_dirs,["apps"]}.
{deps_dir,"deps"}.
{deps, [
    {erlydtl,".*", {git, "git://github.com/evanmiller/erlydtl", {tag, "0.8.0"}  }},
    {cowboy, ".*", {git, "git://github.com/extend/cowboy",      {tag, "1.0.1"}  }},
    {gproc,  ".*", {git, "git://github.com/uwiger/gproc.git",   {tag, "0.3"}    }},
    {fs,     ".*", {git, "git://github.com/synrc/fs",           {tag, "1.9"}    }},
    {sh,     ".*", {git, "git://github.com/synrc/sh",           {tag, "1.9"}    }},
    {mad,    ".*", {git, "git://github.com/synrc/mad",          {tag, "1.9"} }},
    {active, ".*", {git, "git://github.com/synrc/active",       {tag, "1.9"} }},
    {nitro,  ".*", {git, "git://github.com/synrc/nitro",        {tag, "0.9"} }},
    {n2o,    ".*", {git, "git://github.com/synrc/n2o",          {tag, "4.4"} }},
]}.
These are dependencies, which MAD is going to download and place in a deps_dir, which happens to be "deps" as it says on line 2.   As you can see n2o is in the list already.

So now we can just try to run the template app. Type this commands:
cd helloworld
mad dep com pla rep
If everythings runs smoothly you are going to see some console output. And then you can press Enter and there's "1> " line at the bottom, which means Erlang virtual machine started successfully and opened REPL for you.
First argument of  "mad dep com pla rep" command is "dep" which is "fetch all my dependencies from rebar.config". After that comes "com" which is "compile all the files in sub_dirs, and deps_dir".  And then comes "pla" which is "search over the project files and build the plan of applications structure so I can be sure of right start order of my apps"
And finally there's "rep" which is "open the repl".

And now if you look into sys.config you're going to see something like this
[{n2o, [{port,8001},{route,routes},{log_modules,sample}]}].
Aha, the auto-generated template says to start n2o on port 8001. Since we already started our project with MAD let's open "localhost:8001" in the browser.




And here it is, our first app on N2O.

воскресенье, 26 февраля 2017 г.

Quick Tip: find all Django tags in Sublime Text

Here's the quick tip:
1. Open find panel at the bottom: Ctrl (or Cmd on MacOS) + F
2. Toggle Regular Expression button. It has .* on it.
3. And here is the trickiest part: {% block \w+ %}(?s).*?{% endblock \w+ %}
It's the regex for finding all django tags

понедельник, 6 апреля 2015 г.

Mithril.js cram.js curl.js

При использовании библиотеки mithril если использовать только curl, т.е. не собирать js файлы в bundle, то все работает без ошибок. Но если собирать с помощью cram, то mithril.js попадет в bundle, и curl его еще подгрузит дополнительно, т.е. почему-то он не знает, что файл уже в сборке. Это приводит к ошибке "Multiple anonymous defines encountered"
Это лечится следующим конфигом в настройках curl.path:

"mithril": {
            location: "mithril.min.js",
            config: {
                loader: "curl/loader/legacy",
                exports: "m"
            }
        }
 Обратите внимание, что используется legacy loader, несмотря на то, что в mithril.js задан amd конфиг:
if (typeof module != "undefined" && module !== null && module.exports) module.exports = m; else if (typeof define === "function" && define.amd) define(function() {return m});

среда, 10 сентября 2014 г.

buildout gunicorn gevent django 1.7

After upgrading from django 1.5.5 to django 1.7 i couldn't make gunicorn work. It kept telling me (when running bin/django run_gunicorn -c config/gunicorn)
HaltServer Worker failed to boot: 3
and
bin/django runserver
works fine.

After some time I've got working solution for my case. I took code http://gunicorn-docs.readthedocs.org/en/latest/custom.html

As you can see at L06 I have parse func. I am using buildout, which creates django and django.wsgi files in bin/ directory. And in these files sys.path is changed to make your app be able to locate eggs, which are located in eggs/ dir. (e.g. eggs/Django-1.7-py2.7.egg). So gevent is also located there (eggs/gevent-1.0.1-py2.7-linux-x86_64.egg) in my case. So I have to change sys.path, then I do monkey patching with gevent and then with gevent_psycopg2 since I'm using postgresql.


#!/usr/bin/python

from __future__ import unicode_literals
import sys

def parse():
    s = False
    result = []
    with open("bin/django.wsgi", "r") as f:
        for line in f.readlines():
            if line.startswith("sys.path[0:0]"):
                s = True
                continue
            if "]" in line:
                break
            if s:
                result.append(line.strip()[1:-2])
    return result

sys.path[0:0] = parse()

import gevent.monkey
gevent.monkey.patch_all()

import gevent_psycopg2
gevent_psycopg2.monkey_patch()


import multiprocessing

import gunicorn.app.base

from gunicorn.six import iteritems

def number_of_workers():
    return (multiprocessing.cpu_count() * 2) + 1


class StandaloneApplication(gunicorn.app.base.BaseApplication):

    def __init__(self, app, options=None):
        self.options = options or {}
        self.application = app
        super(StandaloneApplication, self).__init__()

    def load_config(self):
        config = dict([(key, value) for key, value in iteritems(self.options)
                       if key in self.cfg.settings and value is not None])
        for key, value in iteritems(config):
            self.cfg.set(key.lower(), value)

    def load(self):
        return self.application


if __name__ == '__main__':
    options = {
        'bind': '%s:%s' % ('0.0.0.0', '8000'),
        'workers': number_of_workers(),
        'worker_class': "gunicorn.workers.ggevent.GeventWorker",
        'debug': True
    }
    import djangorecipe.wsgi

    application = djangorecipe.wsgi.main('homecont.development', logfile='')
    StandaloneApplication(application, options).run()

воскресенье, 13 апреля 2014 г.

Как удалить сессии у пользователя

from django.contrib.sessions.models import Session
from django.contrib.auth.models import User


def close_session(login):
    user = User.objects.get(username=login)
    for session in Session.objects.all():
        uid = session.get_decoded().get('_auth_user_id')
        if uid == user.pk:
            session.delete()

четверг, 3 апреля 2014 г.

Старт django приложения с buildout и gevent

import os, sys, string

dirname = os.path.dirname(__file__)
filepath = os.path.join(dirname, "./bin/django")

filecontent = open(filepath, "r").readlines()
paths = []
for line in filecontent:
line = line.strip()
if line.startswith("'/"):
paths.append(line[1:-2])

sys.path[0:0] = paths

from gevent import monkey; monkey.patch_all()
from gevent.wsgi import WSGIServer

from django.core.management import setup_environ    
os.environ['DJANGO_SETTINGS_MODULE'] = 'homecont.development'
import homecont.settings
setup_environ(homecont.settings)

from django.core.handlers.wsgi import WSGIHandler as DjangoWSGIApp
application = DjangoWSGIApp()
server = WSGIServer(("127.0.0.1", 8000), application)
print "Starting server on http://127.0.0.1:8000"
server.serve_forever()