7
votes

Est-ce la manière approuvée d'accéder aux données adjacentes à / empaquetées avec un script Python?

J'ai un script Python qui a besoin de certaines données qui sont stockées dans un fichier qui sera toujours au même emplacement que le script. J'ai un setup.py pour le script, et je veux m'assurer qu'il est installable dans une grande variété d'environnements, et peut être transformé en un exécutable autonome si nécessaire.

Actuellement, le script fonctionne avec Python 2.7 et Python 3.3 ou supérieur (bien que je n'ai pas d'environnement de test pour 3.3 donc je ne peux pas en être sûr).

J'ai trouvé cette méthode pour obtenir les données. Ce script ne fait pas partie d'un répertoire de module avec __init__.py ou quoi que ce soit, c'est juste un fichier autonome qui fonctionnera s'il est simplement exécuté avec python directement, mais a également un point d'entrée défini dans le fichier setup.py . C'est tout un fichier. Est-ce la bonne manière?

def fetch_wordlist():
    wordlist = 'wordlist.txt'
    try:
        import importlib.resources as res
        return res.read_binary(__file__, wordlist)
    except ImportError:
        pass
    try:
        import pkg_resources as resources
        req = resources.Requirement.parse('makepw')
        wordlist = resources.resource_filename(req, wordlist)
    except ImportError:
        import os.path
        wordlist = os.path.join(os.path.dirname(__file__), wordlist)
    with open(wordlist, 'rb') as f:
        return f.read()

Cela semble ridiculement complexe. En outre, il semble s'appuyer sur le système de gestion des paquets d'une manière avec laquelle je ne suis pas à l'aise. Le script ne fonctionne plus à moins d'avoir été installé par pip, et cela ne semble pas non plus souhaitable.


2 commentaires

Quel est le problème avec l'utilisation uniquement de l'approche basée sur os.path ?


@Leon - Cela ne fonctionne pas si l'objet a été inséré dans un .zip ou si quelqu'un a utilisé py2exe dessus ou quelque chose. Les fichiers de module Python ne sont pas toujours des fichiers sur le système de fichiers, en particulier s'ils sont empaquetés à l'aide de setuptools ou distutils.


3 Réponses :


0
votes

Vous avez raison sur le fait que votre méthode de lecture d'un fichier est un peu inutilement complexe. Sauf si vous avez une raison vraiment spécifique d'utiliser les modules importlib et pkg_resources , c'est plutôt simple.

import os

def fetch_wordlist():
    if not os.path.exists('wordlist.txt'):
        raise FileNotFoundError

    with open('wordlist.txt', 'rb') as wordlist:
        return wordlist.read()

Vous n'avez pas donné beaucoup d'informations concernant votre script, donc je ne peux pas expliquer pourquoi il ne fonctionne pas à moins qu'il ne soit installé en utilisant pip . Ma meilleure supposition: votre script est probablement emballé dans un package python.


2 commentaires

J'ai essayé de clarifier un peu plus. Ce script est actuellement installable par pip, et je souhaite le conserver ainsi. Et si quelqu'un veut le mettre dans un fichier zip, ou dans py2exe ou quelque chose comme ça, je veux qu'il continue à fonctionner.


Oui, cela a plus de sens maintenant. Je dirais que la solution de BPL fonctionnerait très bien dans ce cas.



6
votes

Ressources vivant sur le système de fichiers

La manière standard de lire un fichier adjacent à votre script python serait:

a) Si vous avez python> = 3.4, je vous suggère d'utiliser le module pathlib , comme ceci:

if getattr(sys, 'frozen', False):
    app_path = Path(sys.executable).parent
elif __file__:
    app_path = Path(__file__).parent

Pour votre problème particulier, je vous suggère de jeter un œil à ce https://importlib-resources.readthedocs.io/en/latest/using.html#file-system-or-zip-file . p >

Si vous voulez savoir ce que fait cette bibliothèque derrière les rideaux parce que vous n'êtes pas prêt à installer une bibliothèque tierce, vous pouvez trouver le code pour py2 ici et py3 ici au cas où vous voudriez obtenir les éléments pertinents pour votre problème particulier


8 commentaires

De plus, j'ai clarifié un peu les choses dans ma question. J'aime __file__ , mais cela ne fonctionne pas dans toutes les situations.


Ok, bien vous avez clarifié la question, maintenant cela a plus de sens. Et à propos de pathlib, ce module a été introduit sur la version 3.4. , je vais éditer ma réponse pour l'ajuster à la nouvelle question :)


@Omnifarious A édité ma réponse, j'espère que ça aide


@BPL J'imagine que OP signifie que le fichier de ressources ( wordlist.txt ) est également gelé et se trouve à l'intérieur du paquet plutôt qu'à près de celui-ci.


@Leon - Oui, c'est tout à fait exact. Je ne savais pas trop comment comprendre ce que je voulais dire.


@Omnifarious Edited à nouveau, j'espère que cette fois j'ai pu comprendre complètement votre question :)


@BPL - En effet, cela semble être une réponse complète. Ma politique générale en matière de primes est d'attendre pour les attribuer peu de temps avant leur expiration. Je vais me mettre une alarme. :-) Votre réponse semble essentiellement dire que ce que je fais est en quelque sorte correct, mais est mal organisé. :-)


Pour être honnête avec vous, généralement j'essaie toujours d'éviter d'utiliser des dépendances sur mon logiciel si je suis capable de contrôler tous les cas d'utilisation avec un code minimaliste mais dans ce cas, avoir une bibliothèque tierce telle que importlib_resources qui vous donne une couche uniforme pour gérer à la fois python2.x et python3.x me semble être un choix judicieux. Si je me trouvais dans votre situation, j'utiliserais cette lib. Concernant votre politique générale des primes, je fais aussi de même :). Cela rendra généralement un fil plus riche en contenu. Et quand je juge qui mérite la prime, je considère également le moment où une réponse légitime a été donnée;) Gl!



4
votes

Je vais prendre des précautions et faire une hypothèse car cela peut considérablement simplifier votre problème. La seule façon dont je peux imaginer que vous pouvez prétendre que ces données sont "stockées dans un fichier qui sera toujours au même emplacement que le script" est que vous avez créé ces données, une fois, et les avez placées dans un fichier dans le code source annuaire. Même si ces données sont binaires, avez-vous envisagé de faire des données une chaîne d'octets littérale dans un fichier python, puis de simplement les importer comme vous le feriez pour toute autre chose?


1 commentaires

J'ai. Ce n'est en fait pas binaire, mais pour économiser de l'espace, je l'ai compressé par bzip2 et l'ai stocké dans une chaîne de base64. C'était un peu difficile à manier, mais cela a fonctionné. Je l'ai stocké dans le même fichier que le reste de la source, mais en faire un module séparé fonctionnerait mieux. Je pourrais le faire car cela évite toute la stupidité et la laideur.