A simple filtering syntax tree in Python

Working on various pieces of software those last years, I noticed that there’s always a feature that requires implementing some DSL.

The problem with DSL is that it is never the road that you want to go. I remember how creating my first DSL was fascinating: after using programming languages for years, I was finally designing my own tiny language!

A new language that my users would have to learn and master. Oh, it had nothing new, it was a subset of something, inspired by my years of C, Perl or Python, who knows. And that’s the terrible part about DSL: they are an marvelous tradeoff between the power that they give to users, allowing them to define precisely their needs and the cumbersomeness of learning a language that will be useful in only one specific situation.

In this blog post, I would like to introduce a very unsophisticated way of implementing the syntax tree that could be used as a basis for a DSL. The goal of that syntax tree will be filtering. The problem it will solve is the following: having a piece of data, we want the user to tell us if the data matches their conditions or not.

To give a concrete example: a machine wants to grant the user the ability to filter the beans that it should keep. What the machine passes to the filter is the size of the current grain, and the filter should return either true or false, based on the condition defined by the user: for example, only keep beans that are bigger that are between 1 and 2 centimeters or between 4 and 6 centimeters.

The number of conditions that the users can define could be quite considerable, and we want to provide at least a basic set of predicate operators: equalgreater than and lesser than. We also want the user to be able to combine those, so we’ll add the logical operators or and and.

A set of conditions can be seen as a tree, where leaves are either predicates, and in that case, do not have children, or are logical operators, and have children. For example, the propositional logic formula φ1 ∨ (φ2 ∨ φ3) can be represented with as a tree like this:

Starting with this in mind, it appears that the natural solution is going to be recursive: handle the predicate as terminal, and if the node is a logical operator, recurse over its children.
Since we will be doing Python, we’re going to use Python to evaluate our syntax tree.

The simplest way to write a tree in Python is going to be using dictionaries. A dictionary will represent one node and will have only one key and one value: the key will be the name of the operator (equalgreater thanorand…) and the value will be the argument of this operator if it is a predicate, or a list of children (as dictionaries) if it is a logical operator.

For example, to filter our bean, we would create a tree such as:

{"or": [
  {"and": [
    {"ge": 1},
    {"le": 2},
  ]},
  {"and": [
    {"ge": 4},
    {"le": 6},
  ]},
]}

The goal here is to walk through the tree and evaluate each of the leaves of the tree and returning the final result: if we passed 5 to this filter, it would return True, and if we passed 10 to this filter, it would return False.

Here’s how we could implement a very depthless filter that only handles predicates (for now):

import operator

class InvalidQuery(Exception):
    pass

class Filter(object):
    binary_operators = {
        "eq": operator.eq,
        "gt": operator.gt,
        "ge": operator.ge,
        "lt": operator.lt,
        "le": operator.le,
    }

    def __init__(self, tree):
        # Parse the tree and store the evaluator
        self._eval = self.build_evaluator(tree)

    def __call__(self, value):
        # Call the evaluator with the value
        return self._eval(value)

    def build_evaluator(self, tree):
        try:
            # Pick the first item of the dictionary.
            # If the dictionary has multiple keys/values
            # the first one (= random) will be picked.
            # The key is the operator name (e.g. "eq")
            # and the value is the argument for it
            operator, nodes = list(tree.items())[0]
        except Exception:
            raise InvalidQuery("Unable to parse tree %s" % tree)
        try:
            # Lookup the operator name
            op = self.binary_operators[operator]
        except KeyError:
            raise InvalidQuery("Unknown operator %s" % operator)
        # Return a function (lambda) that takes
        # the filtered value as argument and returns
        # the result of the predicate evaluation
        return lambda value: op(value, nodes)

You can use this Filter class by passing a predicate such as {"eq": 4}:

>>> f = Filter({"eq": 4})
>>> f(2)
False
>>> f(4)
True

This Filter class works but is quite limited as we did not provide logical operators. Here’s a complete implementation that supports binary operators and and or:

import operator


class InvalidQuery(Exception):
    pass


class Filter(object):
    binary_operators = {
        u"=": operator.eq,
        u"==": operator.eq,
        u"eq": operator.eq,

        u"<": operator.lt,
        u"lt": operator.lt,

        u">": operator.gt,
        u"gt": operator.gt,

        u"<=": operator.le,
        u"≤": operator.le,
        u"le": operator.le,

        u">=": operator.ge,
        u"≥": operator.ge,
        u"ge": operator.ge,

        u"!=": operator.ne,
        u"≠": operator.ne,
        u"ne": operator.ne,
    }

    multiple_operators = {
        u"or": any,
        u"∨": any,
        u"and": all,
        u"∧": all,
    }

    def __init__(self, tree):
        self._eval = self.build_evaluator(tree)

    def __call__(self, value):
        return self._eval(value)

    def build_evaluator(self, tree):
        try:
            operator, nodes = list(tree.items())[0]
        except Exception:
            raise InvalidQuery("Unable to parse tree %s" % tree)
        try:
            op = self.multiple_operators[operator]
        except KeyError:
            try:
                op = self.binary_operators[operator]
            except KeyError:
                raise InvalidQuery("Unknown operator %s" % operator)
            return lambda value: op(value, nodes)
        # Iterate over every item in the list of the value linked
        # to the logical operator, and compile it down to its own
        # evaluator.
        elements = [self.build_evaluator(node) for node in nodes]
        return lambda value: op((e(value) for e in elements))

To support the and and or operators, we leverage the all and any built-in Python functions. They are called with an argument that is a generator that evaluates each one of the sub-evaluator, doing the trick.

Unicode is the new sexy, so I’ve also added Unicode symbols support.

And it is now possible to implement our full example:

>>> f = Filter(
...     {"∨": [
...         {"∧": [
...             {"≥": 1},
...             {"≤": 2},
...         ]},
...         {"∧": [
...             {"≥": 4},
...             {"≤": 6},
...         ]},
...     ]})
>>> f(5)
True
>>> f(8)
False
>>> f(1)
True

As an exercise, you could try to add the not operator, which deserve its own category as it is a unary operator!

In the next blog post, we will see how to improve that filter with more features, and how to implement a domain-specific language on top of it, to make humans happy when writing the filter!

Hole and Henni – François Charlier, 2018

In this drawing, the artist represents the deepness of functional programming and how its horse power can help you escape many dark situations.

 

Julien Danjou

https://julien.danjou.info/simple-filtering-syntax-tree-in-python/

Python и Twisted

twistedTwisted — это фреймворк на Python для разработки сетевых приложений, который среди многих других применений, может быть использован и для параллельной обработки данных — мультипроцессности. Это замечательно, но мне пришлось попотеть для того, чтобы найти то, что мне нужно.

twisted_ntwk_pro_ess_comp.indd

Я листал документацию Twisted и книгу O’Reilly Twisted. Существует также рецепт в Python Cookbook. Однако, самое интересное я нашел в статье Брюса Эккель — Параллельность с Python, Twisted и Flex. Также стоит прочитать первоначальные статьи Брюса Эккель про Twisted: Grokking Twisted.

Вот мои замечания о текущем примере Брюса.

Python CookbookЯ убрал Flex — отчасти потому, что мне это не нужно и я ничего не хочу знать об этом. В примере запускается контроллер, который инициализирует ряд отдельных параллельных процессов-вычислителей, в которых уже запускаются какие-то сложные действия (эти процессы называют solvers). Также тут имеется взаимодействие между контроллером и вычислителями. Хотя этот пример запускается только на одной машине, те принципы, о которых говориться в статье — не трудно распространить и на систему из нескольких компьютеров.

Для хорошего примера, как это работает, пожалуйста, смотрите оригинал статьи.

Вот solver.py который скопирован с оригинала. Настоящая «работа» происходит в методе step(). Я только добавил некоторую отладочную информацию для себя.

"""
solver.py
Original version by Bruce Eckel
Solves one portion of a problem, in a separate process on a separate CPU
"""
import sys, random, math
from twisted.spread import pb
from twisted.internet import reactor

class Solver(pb.Root):

    def __init__(self, id):
        print "solver.py %s: solver init" % id
        self.id = id

    def __str__(self): # String representation
        return "Solver %s" % self.id

    def remote_initialize(self, initArg):
        return "%s initialized" % self

    def step(self, arg):
        print "solver.py %s: solver step" % self.id
        "Simulate work and return result"
        result = 0
        for i in range(random.randint(1000000, 3000000)):
            angle = math.radians(random.randint(0, 45))
            result += math.tanh(angle)/math.cosh(angle)
        return "%s, %s, result: %.2f" % (self, str(arg), result)

    # Alias methods, for demonstration version:
    remote_step1 = step
    remote_step2 = step
    remote_step3 = step

    def remote_status(self):
        print "solver.py %s: remote_status" % self.id
        return "%s operational" % self

    def remote_terminate(self):
        print "solver.py %s: remote_terminate" % self.id
        reactor.callLater(0.5, reactor.stop)
        return "%s terminating..." % self

if __name__ == "__main__":
    port = int(sys.argv[1])
    reactor.listenTCP(port, pb.PBServerFactory(Solver(sys.argv[1])))
    reactor.run()

Вот controller.py. Он также скопирован из оригинальной статьи, но я убрал Flex и создал сигналы start и terminate в классе контроллера. Я не уверен, что это имеет смысл, но, по крайней мере, это позволило мне нормально использовать пример. Я также перенес метод terminate из FlexInterface в Controller.

"""
Controller.py
Original version by Bruce Eckel
Starts and manages solvers in separate processes for parallel processing.
"""
import sys
from subprocess import Popen
from twisted.spread import pb
from twisted.internet import reactor, defer

START_PORT = 5566
MAX_PROCESSES = 2

class Controller(object):

    def broadcastCommand(self, remoteMethodName, arguments, nextStep, failureMessage):
        print "controller.py: broadcasting..."
        deferreds = [solver.callRemote(remoteMethodName, arguments) 
                     for solver in self.solvers.values()]
        print "controller.py: broadcasted"
        reactor.callLater(3, self.checkStatus)

        defer.DeferredList(deferreds, consumeErrors=True).addCallbacks(
            nextStep, self.failed, errbackArgs=(failureMessage))

    def checkStatus(self):
        print "controller.py: checkStatus"
        for solver in self.solvers.values():
            solver.callRemote("status").addCallbacks(
                lambda r: sys.stdout.write(r + "\n"), self.failed, 
                errbackArgs=("Status Check Failed"))

    def failed(self, results, failureMessage="Call Failed"):
        print "controller.py: failed"
        for (success, returnValue), (address, port) in zip(results, self.solvers):
            if not success:
                raise Exception("address: %s port: %d %s" % (address, port, failureMessage))

    def __init__(self):
        print "controller.py: init"
        self.solvers = dict.fromkeys(
            [("localhost", i) for i in range(START_PORT, START_PORT+MAX_PROCESSES)])
        self.pids = [Popen(["python", "solver.py", str(port)]).pid
                     for ip, port in self.solvers]
        print "PIDS: ", self.pids
        self.connected = False
        reactor.callLater(1, self.connect)

    def connect(self):
        print "controller.py: connect"
        connections = []
        for address, port in self.solvers:
            factory = pb.PBClientFactory()
            reactor.connectTCP(address, port, factory)
            connections.append(factory.getRootObject())
        defer.DeferredList(connections, consumeErrors=True).addCallbacks(
            self.storeConnections, self.failed, errbackArgs=("Failed to Connect"))

        print "controller.py: starting parallel jobs"
        self.start()

    def storeConnections(self, results):
        print "controller.py: storeconnections"
        for (success, solver), (address, port) in zip(results, self.solvers):
            self.solvers[address, port] = solver
        print "controller.py: Connected; self.solvers:", self.solvers
        self.connected = True

    def start(self):
        "controller.py: Begin the solving process"
        if not self.connected:
            return reactor.callLater(0.5, self.start)
        self.broadcastCommand("step1", ("step 1"), self.step2, "Failed Step 1")

    def step2(self, results):
        print "controller.py: step 1 results:", results
        self.broadcastCommand("step2", ("step 2"), self.step3, "Failed Step 2")

    def step3(self, results):
        print "controller.py: step 2 results:", results
        self.broadcastCommand("step3", ("step 3"), self.collectResults, "Failed Step 3")

    def collectResults(self, results):
        print "controller.py: step 3 results:", results
        self.terminate()

    def terminate(self):
        print "controller.py: terminate"
        for solver in self.solvers.values():
            solver.callRemote("terminate").addErrback(self.failed, "Termination Failed")
        reactor.callLater(1, reactor.stop)
        return "Terminating remote solvers"

if __name__ == "__main__":
    controller = Controller()
    reactor.run()

Чтобы запустить программу, положите оба файла в одну папку и запустите

python controller.py

Вы должны увидеть, как загрузка двух процессоров (если их, конечно, у вас — 2) поднимется до 100%. А вот и вывод скрипта на экран:

controller.py: init
PIDS:  [12173, 12174]
solver.py 5567: solver init
solver.py 5566: solver init
controller.py: connect
controller.py: starting parallel jobs
controller.py: storeconnections
controller.py: Connected; self.solvers: {('localhost', 5567): , ('localhost', 5566): }
controller.py: broadcasting...
controller.py: broadcasted
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 1 results: [(True, 'Solver 5567, step 1, result: 683825.75'), (True, 'Solver 5566, step 1, result: 543177.17')]
controller.py: broadcasting...
controller.py: broadcasted
Solver 5567 operational
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 2 results: [(True, 'Solver 5567, step 2, result: 636793.90'), (True, 'Solver 5566, step 2, result: 335358.16')]
controller.py: broadcasting...
controller.py: broadcasted
Solver 5567 operational
solver.py 5566: solver step
solver.py 5567: solver step
controller.py: checkStatus
solver.py 5566: remote_status
Solver 5566 operational
solver.py 5567: remote_status
controller.py: step 3 results: [(True, 'Solver 5567, step 3, result: 847386.43'), (True, 'Solver 5566, step 3, result: 512120.15')]
controller.py: terminate
Solver 5567 operational
solver.py 5566: remote_terminate
solver.py 5567: remote_terminate

Интерфейсы к SOCKS5

anonymous-proxy-server

PHP

Используется cURL:

$ch=curl_init("http://www.fbi.gov");

   @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   // возвращаем результат, а не выводим его в печать
   @curl_setopt($ch, CURLOPT_VERBOSE, 0);
   //запрещаем выводить подробные сообщения о всех действиях
   @curl_setopt($ch, CURLOPT_HEADER, 0);
   // запрещаем Headers
   @curl_setopt($ch, CURLOPT_PROXY, "192.168.0.100:47047");
   //IP адрес и порт SOCKS5
   @curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); 
   //включаем использование SOCKS5
   $x=@curl_exec($ch);

curl_close($ch);

echo $x;
//выводим содержимое страницы в браузер

Python

Используем SocksiPy через wrapmodule()

1. Импортируем SocksiPy и другие модули
2. Запускаем setdefaultproxy() и передаем нужные данные
3. Загружаем целевой модуль внутри модуля wrapmodule()

# Импортируем нужные модули
import ftplib
import telnetlib
import urllib2

# Импортитуем SocksiPy
import socks

# Загружаем прокси
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, 'localhost', 9050)

# Перенаправляем FTP-сессию через SOCKS прокси
socks.wrapmodule(ftplib)
ftp = ftplib.FTP('cdimage.ubuntu.com')
ftp.login('anonymous', 'chicago@ic.fbi.gov')
print ftp.dir('cdimage')
ftp.close()

# Перенаправляем соединение telnet через SOCKS прокси
socks.wrapmodule(telnetlib)
tn = telnetlib.Telnet('achaea.com')
print tn.read_very_eager()
tn.close()

# Перенаправляем HTTP-запросы через SOCKS прокси
socks.wrapmodule(urllib2)
print urllib2.urlopen('http://www.whatismyip.com/automation/n09230945.asp').read()

 

Используем SocksiPy через socksocket

1. Импортируем SocksiPy
2. Загружаем socks.socksocket, который возвращает несколько параметров для socket.socket.
3. Запускаем setproxy() и передаем нужные данные
4. Подключаемся и используем сокет, как обычный…

import socks
s = socks.socksocket()
s.setproxy(socks.PROXY_TYPE_SOCKS5, 'localhost', 9050)
s.connect(('www.whatismyip.com', 80))
s.send('GET /automation/n09230945.asp HTTP/1.1\r\n\r\n')

data = ''
buf = s.recv(1024)
while len(buf):
data += buf
buf = s.recv(1024)
s.close()

print("Connected from %s." % (data))

virtualenv

На официальном сайте virtualenv в разделе «Installation» рекомендуется устанавливать virtualenv через менеджер Python-пакетов pip (командой «pip install virtualenv«). Однако далеко не всегда pip установлен в системе по-умолчанию. Я не стал исключением: команду pip система не понимает. Идем на официальный сайт и видим, что pip рекомендуется использовать в пределах виртуального окружения virtualenv. При установке virtualenv, pip устанавливается автоматически. Выходит, официальные сайты обеих программ рекомендуют устанавливать virtualenv через pip, а pip — через virtualenv.

Примечание: есть, конечно, множество других способов установки того и другого (через easy_install, скачивание deb-пакетов или python-установщиков) —  все эти способы также описаны на официальных сайтах или на чьих-то блогах. Но все-таки что-то тянет меня придерживаться рекомендуемых способов от официальных разработчиков.

Если следовать концепции виртуальных окружений — логично использовать pip в пределах virtualenv, а не глобально во всей системе. Тем более нахаляву, что и поставится он автоматически вместе с virtualenv. Значит, прежде всего нужно устанавливать virtualenv. Как?

Лучшее решение — установка из репозиториев (почему-то этот вариант не упоминается на официальном сайте virtualenv).

Python 2.x

1. Для установки virtualenv набираем в терминале:

sudo apt-get install python-virtualenv

2. Создаем папку, внутри которой будут храниться папки будущих виртуальных окружений. Лучше всего создать такую папку в пределах своей домашней директории, чтобы не было проблем с правами доступа. Пусть эта папка будет называться «projects«:

mkdir projects

3. Создаем виртуальное окружение внутри папки projects. Пусть наше первое виртуальное окружение будет называться «project_one«.

cd projects
virtualenv project_one

(Аналогично могут создаваться виртуальные окружения для каких-то других проектов).

В результате внутри папки /projects/project_one/ создастся маленькая рабочая среда с папками bin/, include/, lib/, local/, содержащими минимальный «набор джентльмена» для работы — python, менеджеры пакетов pip и easy_install. Сюда же могут доставляться все необходимые пакеты, фреймворки (в том числе Django) и утилиты. В пределах каждого виртуального окружения они будут изолированы друг от друга, не оказывая никакого взаимного «паразитного» влияния.

Примечание: во многих руководствах по работе с виртуальными окружениями рекомендуется выполнять команду virtualenv с ключом —no-site-packages. Применение этого ключа позволяет создавать виртуальное окружение, изолированное от системной питоновской папки site-packages, что повышает степень автономности. Так вот в новых версиях virtualenv указывать этот ключ не обязательно, поскольку в них эта опция включена по-умолчанию.

4. Для активации нужно виртуального окружения нужно зайти в его папку («cd project_one») и выполнить следующее:

source bin/activate

После активации командная строка изменится: перед именем пользователя появится название виртуального окружения в скобках «(project_one)имя_пользователя>@имя_компьютера ~«.

Теперь любые команды по установке пакетов (например, «pip install django«) или их удалению будут выполнятся только в пределах активированного окружения.

Для выхода из виртуального окружения и перехода в обычный режим достаточно набрать:

deactivate

 

Python 3.x

$ python3.4 -m venv --without-pip env
$ cd env
$ source ./bin/activate # virtualenv activated

$ wget https://bootstrap.pypa.io/get-pip.py # get installation script for pip
$ python3.4 get-pip.py
$ deactivate
$ source venvdir/bin/activate

$ pip list # just to check that pip works!

 

Старт проекта на Django

Прежде всего убедитесь, что у вас подключена услуга «Поддержка Python + Django». На время разработки вам также часто будет нужен доступ по SSH, поэтому перед созданием нового Django-проекта подключите и услугу «Поддержка SSH». Если в качестве базы данных вы будете использовать MySQL, соответствующая услуга также должна быть подключена.

  1. Подключитесь к серверу по SSH и создайте и активируйте виртуальное окружение Python (если вы создаете не первый проект и в качестве виртуального окружения хотите использовать уже существующее, пропустите этот и следующие два шага). Введите команды:
    virtualenv-2.7 virtualenv/MyEnv
    . virtualenv/MyEnv/bin/activate

    В результате будет создана папка virtualenv/MyEnv. Вместо MyEnv вы можете выбрать и любое другое имя виртуального окружения.

  2. В рамках виртуального окружения установите свежую версию Django:pip 
    install --upgrade django

    Можно установить и любую другую версию. Например, 1.0:

    pip install --upgrade django==1.0

    Таким же образом можно установить и любые другие модули Python.

  3. Создайте папку, где будут располагаться ваши проекты. Эта папка должна находиться вне DOCUMENT_ROOT, то есть вне папок вида domains/имя_домена. Лучшим вариантом будет создать папку django рядом с директорией domains:
    mkdir django
  4. Перейдите в папку с проектами и создайте новый проект:
    cd django
    django-admin.py startproject имя_проекта

    В результате будет создана папка имя_проекта со стандартным шаблоном Django-проекта.

  5. Откройте файл settings.py и измените в нем значения необходимых переменных. В качестве значения переменной STATIC_ROOT укажите
    os.path.join(os.path.expanduser('~'), 'domains/имя_домена/static/')

    добавив в самое начало файла строку

    import os

    Внимание! Если вы будете использовать команду syncdb через SSH, то при использовании MySQL в качестве движка баз данных в поле HOST словаря DATABASES[‘default’] обязательно нужно указать IP 127.0.0.1. Подробнее об этом здесь.

    Более подробную информацию о переменных, доступных для редактирования в settings.py, можно найти в документации Django.

  6. Если вы будете использовать mod_wsgi (предпочтительный вариант):

    В директории домена, на котором будет находиться ваш проект (domains/имя_домена) создайте файл django.wsgi и поместите в него такие строки:

    import os, sys
    virtual_env = os.path.expanduser('~/virtualenv/MyEnv')
    activate_this = os.path.join(virtual_env, 'bin/activate_this.py')
    execfile(activate_this, dict(__file__=activate_this))
    sys.path.insert(0, os.path.join(os.path.expanduser('~'), 'django/имя_проекта'))
    os.environ['DJANGO_SETTINGS_MODULE'] = 'имя_проекта.settings'
    import django.core.handlers.wsgi
    application = django.core.handlers.wsgi.WSGIHandler()

    Примечание. Обратите также внимание на пятую и шестую строки этого кода: если вы в своих проектах обычно не используете имя проекта внутри оператора import (например,«from имя_приложения.models import *», а не «from имя_проекта.имя_приложения.models import *» в файле views.py), то указывать имя проекта в пятой строке нужно два раза, а во второй, наоборот, не нужно:

    sys.path.insert(0, os.path.join(os.path.expanduser('~'), 'django/имя_проекта/имя_проекта'))
    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

    Вернитесь к этому примечанию, если будете получать ошибки вида «ImportError: No module named…»

    Затем создайте в той же папке еще один файл — .htaccess — и поместите в него следующие директивы:

    AddHandler wsgi-script .wsgi
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ /django.wsgi/$1 [QSA,PT,L]
  7. Если вы будете использовать mod_python (не рекомендуется):

    В папке с проектами (django) создайте файл modpython_virtualenv.py с таким содержимым внутри:

    import os
    virtual_env = os.path.expanduser('~/virtualenv/MyEnv')
    activate_this = os.path.join(virtual_env, 'bin/activate_this.py')
    execfile(activate_this, dict(__file__=activate_this))
    from django.core.handlers.modpython import handler

    В директории домена, на котором будет находиться ваш проект (domains/имя_домена) создайте файл .htaccess и поместите в него такие строки:

    AddDefaultCharset utf-8
    SetHandler python-program
    PythonHandler modpython_virtualenv
    SetEnv DJANGO_SETTINGS_MODULE имя_проекта.settings
    PythonPath "['/home/usersX/первая_буква_логина/логин/django/имя_проекта'] + sys.path"
    PythonDebug On
    PythonAutoReload On

    Примечание. Здесь и далее X в имени директории usersX может быть целым числом (1, 2, …) или вообще отсутствовать. Точное значение для вашего аккаунта уточняйте в разделе «Техподдержка / Техническая информация» контрольной панели аккаунта (смотрите значение параметра «Домашняя директория»).

    Позже, когда проект будет запускаться в публичную эксплуатацию, не забудьте поменять значения директив PythonDebug и PythonAutoReload на Off — это предотвратит демонстрацию отладочной информации об ошибках всем посетителям и несколько ускорит работу сайта.

    Примечание. Обратите также внимание на директивы SetEnv и PythonPath: если вы в своих проектах обычно не используете имя проекта внутри оператора import (например,«from имя_приложения.models import *», а не «from имя_проекта.имя_приложения.models import *» в файле views.py), то указывать имя проекта в директиве SetEnv не нужно, а в PythonPath, наоборот, нужно два раза:

    SetEnv DJANGO_SETTINGS_MODULE settings
    PythonPath "['/home/usersX/первая_буква_логина/логин/django/имя_проекта/имя_проекта'] + sys.path"

    Кроме того, поместите файл modpython_virtualenv.py внутрь папки проекта.

    Вернитесь к этому примечанию, если будете получать ошибки вида «ImportError: No module named…»

    Далее, во всех папках в директории домена, в которых будут содержаться статические файлы (например, static), нужно также создать по файлу .htaccess с такой строкой внутри:

    SetHandler None

     

На этом установка проекта завершена. Теперь вы можете создавать приложения и приступать к разработке.

Если же в результате выполнения этих действий получаете в браузере ошибку 500, загляните в раздел «Статистика / Лог-файлы».