Il modo migliore per impostare sys.path per lo sviluppo della "hot library" in Python?

3

Ho la mia fonte Python strutturata come segue:

+-branchname/
  +-dst/
  +-src/
  | +-library/
  | | +-cleese/
  | | | +-test/
  | | | | +-__init__.py
  | | | | +-test_cleese.py
  | | | +-__init__.py
  | | | +-cleese.py
  | | +-palin/
  | |   +-test/
  | |   | +-__init__.py 
  | |   | +-test_palin.py 
  | |   +-__init__.py
  | |   +-palin.py
  | +-productline/
  |    +-circus/
  |    | +-test/
  |    | | +-__init__.py
  |    | | +-test_circus.py
  |    | +-__init__.py
  |    | +-circus.py
  |    +-grail/
  |      +-test/
  |      | +-__init__.py
  |      | +-test_grail.py
  |      +-__init__.py
  |      +-grail.py
  +-branch_root_marker

I punti di ingresso sono in circo / e grail /, così come (possibilmente) ciascuno dei test / directory, a seconda di come viene eseguito il test.

Ho più copie di questo albero di sorgenti presenti sullo storage locale in qualsiasi momento (corrispondente a vari rami di manutenzione e funzionalità), quindi non posso impostare PYTHONPATH nella mia shell senza alcun dolore. (Avrei bisogno di ricordarmi di cambiarlo ogni volta che sono passato a lavorare su un ramo diverso, e sono molto smemorato)

Invece, ho una logica che risalga la struttura dei file, iniziando dalla posizione del file "corrente", passando da una foglia all'altra, cercando branch_root_marker. Una volta trovata la directory root della copia di lavoro corrente, aggiunge la libreria / e productline / a sys.path. Io chiamo questa funzione da ogni entry-point nel sistema.

"""Add working copy (branch) to sys.path"""

import os
import sys


def _setpath():
    """Add working copy (branch) to sys.path"""

    markerfile = "branch_root_marker"
    path = ""
    if ("__file__" in locals() or globals()) and __file__ != "__main__":
        path = __file__
    elif sys.argv and sys.argv[0] and not sys.argv[0] == "-c":
        path = sys.argv[0]
    path = os.path.realpath(os.path.expandvars(path))
    while os.path.exists(path):
        if os.path.exists(os.path.join(path, markerfile)):
            break
        else:
            path = os.path.dirname(path)
        errormsg = " ".join(("Could not find", markerfile))
        assert os.path.exists(path) and path != os.path.dirname(path), errormsg

    path = os.path.join(path, "src")
    (_, subdir_list, _) = os.walk(path).next()
    for subdir in subdir_list:
        if subdir.startswith("."):
            continue
        subdir_path = os.path.join(path, subdir)
        if subdir_path in sys.path:
            continue
        sys.path.append(subdir_path)
_setpath()

Attualmente, ho bisogno di mantenere una copia separata ma identica di questa funzione in ogni punto di ingresso. Anche se è una funzione piuttosto breve, sono piuttosto angosciato dal modo in cui fragrante il principio di ESSERE VIOLATO viene violato da questo approccio, e mi piacerebbe trovare un modo per mantenere la logica di modifica di sys.path in un unico punto. Qualche idea?

Nota: - Una cosa che viene in mente è installare la logica di modifica sys.path in una posizione comune che è sempre su PYTHONPATH. Anche se questa non è una pessima idea, significa introdurre una fase di installazione che deve essere eseguita ogni volta che mi muovo in un ambiente fresco; un'altra cosa da ricordare (o, più probabilmente, dimenticare), quindi vorrei evitare questo, se possibile.

    
posta William Payne 07.05.2012 - 16:58
fonte

5 risposte

1

OK, ho iniziato a creare una soluzione che coinvolgesse i PEP 302 ganci di importazione. Troppo complicato.

Penso che tu abbia risposto alla tua stessa domanda con:

this "gateway" script could implement all manner of bootstrapping functionality - for example, ensuring that virtualenv is set up correctly and activated for the current branch.

se stai già usando virtualenv, metti la logica che hai nello script "~ / venv / or-whatever-path / bin / activate" che è già lì. Suggerirei di elencare i possibili punti di ingresso e chiedere una risposta (farà una lista con i numeri e una dichiarazione raw_input () se non ne specifichi uno.

Metterei anche il nome del tuo ramo in $ PS1 in modo che si presenti nel nome della scheda o nel titolo della finestra del tuo terminale. Ho qualcosa di simile e cambio il colore del prompt in modo da non fare la cosa giusta nel posto sbagliato.

    
risposta data 16.05.2012 - 00:33
fonte
2

Devi usare virtualenv . Crea un ambiente python isolato. È anche consigliato quando hai bisogno di versioni diverse di pacchetti di terze parti a seconda della versione del tuo prodotto.

    
risposta data 12.05.2012 - 22:03
fonte
1

Il mio flusso di lavoro è in qualche modo simile al tuo: ho più filiali controllate in diverse directory. Ho provato a creare un virtualenv per ogni ramo, ma è troppo fastidio.

Ho finito per impostare PYTHONPATH relativo a $PWD poiché ho solo un codice eseguibile a un livello di directory: PYTHONPATH=$PWD/.. python code-to-run.py . Questo utilizza l'albero dei sorgenti della dir attuale e, dopo di essa, i moduli virtualenv e di sistema.

Questo non funziona in Windows, ovviamente.

Un altro modo in cui ho provato era un file .pth 'auto-modificante'; includeva

import sys, os; sys.path.insert(0. os.path.abspath('..'))

Potresti utilizzare una logica più complessa, se lo desideri.

    
risposta data 07.05.2012 - 19:07
fonte
1

Il tuo controllo di revisione ti consente di eseguire uno script su sync / clone / update? In tal caso, è possibile configurare i file appropriati in ciascuna directory dell'albero dei sorgenti in modo che la directory root venga referenziata in modo esplicito.

Ad esempio, se stai usando mercurial, questo potrebbe essere d'aiuto, dal link :

  1. Evita di unire file autogenerati (binari) (PDF)
  2. Creazione di pdf al volo

Questo presuppone che tu voglia sempre avere i PDF che puoi usare, ma che non hai bisogno di rintracciarli - solo il loro contenuto (e quelli sono definiti nei file tex).

Per questo aggiungi un hook di aggiornamento che cripta il pdf ogni volta che aggiorni a una revisione.

Modifica il tuo .hg / hgrc per includere la sezione degli hook con un hook di aggiornamento:

[ganci] update.create_pdfs = latex your_tex_file.tex Per renderlo ancora più semplice, puoi usare uno script con versione che crea tutto il pdf. in questo modo puoi semplicemente chiamare lo script e non devi preoccuparti di modificare il file .hg / hgrc quando aggiungi file di testo o cambia la chiamata.

Uso uno script python per la compatibilità della piattaforma:

parse_latex.py:

Cambia i numeri di riga    1 #! / Usr / bin / env python    2 dalla chiamata di importazione del sottoprocesso    3 per i in ("file1.tex", "file2.tex"):    4 call (["latex", i]) .hg / hgrc:

[ganci] update.create = ./parse_latex.py

    
risposta data 09.05.2012 - 21:36
fonte
0

Ok, quindi sono andato con lo script del gateway. Al momento ha solo funzionalità minime, niente cache dei percorsi, nessun controllo virtualenv, ecc. Ma funziona e costituirà la base per qualcosa che crescerà nel tempo.

import os
import sys
import fnmatch

OK = 0
ERROR = 1

_EXCLUDE_FROM_SYSPATH = [".svn", ".hg", ".", ".."]


# -----------------------------------------------------------------------------
def _root_path_of_repo_branch():
    """Returns working copy root path."""

    branch_root_marker = "branch_root_marker"

    path = ""
    if ("__file__" in locals() or globals()) and "__main__" != __file__:
        path = __file__
    elif sys.argv and sys.argv[0] and not sys.argv[0] == "-c":
        path = sys.argv[0]
    path = os.path.realpath(os.path.expandvars(path))
    while os.path.exists(path):
        if os.path.exists(os.path.join(path, branch_root_marker)):
            break
        else:
            path = os.path.dirname(path)
    assert os.path.exists(path), " ".join("Could not find", branch_root_marker)
    return path


# -----------------------------------------------------------------------------
def _pythondirs_in_branch(root_path_of_branch):
    """Find pythondirs in the specified branch."""

    srcpath = os.path.join(root_path_of_branch, "src")
    srctree = os.walk(srcpath)

    pythondirs = list()
    for (dirpath, dirnames, filenames) in srctree:
        for name in _EXCLUDE_FROM_SYSPATH:
            if name in dirnames:
                dirnames.remove(name)
        if any((fnmatch.fnmatch(name, "*.py") for name in filenames)):
            pythondirs.append(dirpath)
    return pythondirs


# -----------------------------------------------------------------------------
def _add_dirs_to_syspath(dirlist):
    """Adds specified directories to the sys path."""

    for dirname in dirlist:
        sys.path.append(dirname)


# -----------------------------------------------------------------------------
def _add_pythondirs_in_branch_to_syspath():
    """Adds pythondirs in the current branch to the sys path"""

    branch_path = _root_path_of_repo_branch()
    pythondirs = _pythondirs_in_branch(branch_path)
    _add_dirs_to_syspath(pythondirs)


# -----------------------------------------------------------------------------
def main(argv=None):
    """Read config & start app."""

    if argv is None:
        argv = sys.argv[1:]

    command_name = argv[0]
    if "test" in command_name:
        pass

    _add_pythondirs_in_branch_to_syspath()

    try:
        command = __import__(command_name)
    except ImportError, err:
        print "IMPORT ERROR : " + err.message
        return ERROR

    return command.main(argv)


if __name__ == "__main__":
    sys.exit(main())
    
risposta data 24.05.2012 - 19:14
fonte

Leggi altre domande sui tag