标签归档:environment-variables

检查环境变量是否存在的良好实践是什么?

问题:检查环境变量是否存在的良好实践是什么?

我想检查我的环境中是否存在"FOO"Python 中的变量。为此,我正在使用os标准库。阅读图书馆的文档后,我想出了两种实现目标的方法:

方法1:

if "FOO" in os.environ:
    pass

方法2:

if os.getenv("FOO") is not None:
    pass

我想知道哪种方法是好的/首选条件,以及为什么。

I want to check my environment for the existence of a variable, say "FOO", in Python. For this purpose, I am using the os standard library. After reading the library’s documentation, I have figured out 2 ways to achieve my goal:

Method 1:

if "FOO" in os.environ:
    pass

Method 2:

if os.getenv("FOO") is not None:
    pass

I would like to know which method, if either, is a good/preferred conditional and why.


回答 0

使用第一个;它直接尝试检查是否在中定义了某些内容environ。尽管第二种形式同样可以很好地工作,但是它在语义上是不足的,因为如果存在,您会得到一个返回的值,并且将其用于比较。

你想看看是否有存在 environ,为什么你会得到只是为了进行比较,然后折腾它扔掉

那正是这样getenv做的:

获取一个环境变量None如果不存在则返回。可选的第二个参数可以指定备用默认值。

(这也意味着您的支票可能只是if getenv("FOO")

你不想得到它,你想检查它的存在。

无论哪种方式,getenv都只是一个包装,environ.get但是您看不到有人通过以下方式检查映射中的成员身份:

from os import environ
if environ.get('Foo') is not None:

总结一下,使用:

if "FOO" in os.environ:
    pass

如果您只想检查是否存在,请使用,getenv("FOO")如果您确实想用可能获得的价值做某事。

Use the first; it directly tries to check if something is defined in environ. Though the second form works equally well, it’s lacking semantically since you get a value back if it exists and only use it for a comparison.

You’re trying to see if something is present in environ, why would you get just to compare it and then toss it away?

That’s exactly what getenv does:

Get an environment variable, return None if it doesn’t exist. The optional second argument can specify an alternate default.

(this also means your check could just be if getenv("FOO"))

you don’t want to get it, you want to check for it’s existence.

Either way, getenv is just a wrapper around environ.get but you don’t see people checking for membership in mappings with:

from os import environ
if environ.get('Foo') is not None:

To summarize, use:

if "FOO" in os.environ:
    pass

if you just want to check for existence, while, use getenv("FOO") if you actually want to do something with the value you might get.


回答 1

两种解决方案都有一种情况,这取决于您要根据环境变量的存在来执行什么操作。

情况1

如果您想纯粹基于环境变量的存在而采取不同的措施而又不关心其价值,那么第一个解决方案就是最佳实践。它简要描述了您要测试的内容:环境变量列表中的’FOO’。

if 'KITTEN_ALLERGY' in os.environ:
    buy_puppy()
else:
    buy_kitten()

情况二

如果您想在环境变量中未定义该值的情况下设置默认值,则第二个解决方案实际上很有用,尽管它不是您编写的形式:

server = os.getenv('MY_CAT_STREAMS', 'youtube.com')

也许

server = os.environ.get('MY_CAT_STREAMS', 'youtube.com')

请注意,如果您的应用程序有多个选项,则可能需要查看ChainMap,它允许根据键合并多个字典。ChainMap文档中有一个示例:

[...]
combined = ChainMap(command_line_args, os.environ, defaults)

There is a case for either solution, depending on what you want to do conditional on the existence of the environment variable.

Case 1

When you want to take different actions purely based on the existence of the environment variable, without caring for its value, the first solution is the best practice. It succinctly describes what you test for: is ‘FOO’ in the list of environment variables.

if 'KITTEN_ALLERGY' in os.environ:
    buy_puppy()
else:
    buy_kitten()

Case 2

When you want to set a default value if the value is not defined in the environment variables the second solution is actually useful, though not in the form you wrote it:

server = os.getenv('MY_CAT_STREAMS', 'youtube.com')

or perhaps

server = os.environ.get('MY_CAT_STREAMS', 'youtube.com')

Note that if you have several options for your application you might want to look into ChainMap, which allows to merge multiple dicts based on keys. There is an example of this in the ChainMap documentation:

[...]
combined = ChainMap(command_line_args, os.environ, defaults)

回答 2

为了安全起见

os.getenv('FOO') or 'bar'

上述答案的一个极端情况是设置了环境变量但为空

对于这种特殊情况,您会得到

print(os.getenv('FOO', 'bar'))
# prints new line - though you expected `bar`

要么

if "FOO" in os.environ:
    print("FOO is here")
# prints FOO is here - however its not

为了避免这种情况,只需使用 or

os.getenv('FOO') or 'bar'

然后你得到

print(os.getenv('FOO') or 'bar')
# bar

什么时候有空的环境变量?

您忘记在.env文件中设置值

# .env
FOO=

或导出为

$ export FOO=

或忘记设置它 settings.py

# settings.py
os.environ['FOO'] = ''

更新:如果有疑问,请查看这些单线

>>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar'))

$ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))"

To be on the safe side use

os.getenv('FOO') or 'bar'

A corner case with the above answers is when the environment variable is set but is empty

For this special case you get

print(os.getenv('FOO', 'bar'))
# prints new line - though you expected `bar`

or

if "FOO" in os.environ:
    print("FOO is here")
# prints FOO is here - however its not

To avoid this just use or

os.getenv('FOO') or 'bar'

Then you get

print(os.getenv('FOO') or 'bar')
# bar

When do we have empty environment variables?

You forgot to set the value in the .env file

# .env
FOO=

or exported as

$ export FOO=

or forgot to set it in settings.py

# settings.py
os.environ['FOO'] = ''

Update: if in doubt, check out these one-liners

>>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar'))

$ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))"

回答 3

如果您要检查是否未设置多个环境变量,可以执行以下操作:

import os

MANDATORY_ENV_VARS = ["FOO", "BAR"]

for var in MANDATORY_ENV_VARS:
    if var not in os.environ:
        raise EnvironmentError("Failed because {} is not set.".format(var))

In case you want to check if multiple env variables are not set, you can do the following:

import os

MANDATORY_ENV_VARS = ["FOO", "BAR"]

for var in MANDATORY_ENV_VARS:
    if var not in os.environ:
        raise EnvironmentError("Failed because {} is not set.".format(var))

回答 4

我的评论可能与给定的标签无关。但是,我是从搜索中转到此页面的。我一直在寻找R中的类似支票,并在@hugovdbeg帖子的帮助下提出了以下内容。我希望这对在R中寻求类似解决方案的人有所帮助

'USERNAME' %in% names(Sys.getenv())

My comment might not be relevant to the tags given. However, I was lead to this page from my search. I was looking for similar check in R and I came up the following with the help of @hugovdbeg post. I hope it would be helpful for someone who is looking for similar solution in R

'USERNAME' %in% names(Sys.getenv())

在Python脚本中,如何设置PYTHONPATH?

问题:在Python脚本中,如何设置PYTHONPATH?

我知道如何在/ etc / profile和环境变量中进行设置。

但是,如果我想在脚本中进行设置怎么办?是导入os,sys吗?我该怎么做?

I know how to set it in my /etc/profile and in my environment variables.

But what if I want to set it during a script? Is it import os, sys? How do I do it?


回答 0

您没有设置PYTHONPATH,而是向中添加条目sys.path。这是应该在其中搜索Python软件包的目录列表,因此您只需将目录追加到该列表即可。

sys.path.append('/path/to/whatever')

实际上,sys.path是通过分割PYTHONPATH路径分隔符:上的值来初始化的(在类似Linux的系统上,;在Windows上)。

您也可以使用来添加目录site.addsitedir,该方法还将考虑.pth您传递的目录内存在的文件。(对于您在中指定的目录,情况并非如此PYTHONPATH。)

You don’t set PYTHONPATH, you add entries to sys.path. It’s a list of directories that should be searched for Python packages, so you can just append your directories to that list.

sys.path.append('/path/to/whatever')

In fact, sys.path is initialized by splitting the value of PYTHONPATH on the path separator character (: on Linux-like systems, ; on Windows).

You can also add directories using site.addsitedir, and that method will also take into account .pth files existing within the directories you pass. (That would not be the case with directories you specify in PYTHONPATH.)


回答 1

您可以通过os.environ以下方式获取和设置环境变量:

import os
user_home = os.environ["HOME"]

os.environ["PYTHONPATH"] = "..."

但是,由于您的解释器已经在运行,因此不会起作用。你最好用

import sys
sys.path.append("...")

这是您PYTHONPATH将在解释程序启动时转换为的数组。

You can get and set environment variables via os.environ:

import os
user_home = os.environ["HOME"]

os.environ["PYTHONPATH"] = "..."

But since your interpreter is already running, this will have no effect. You’re better off using

import sys
sys.path.append("...")

which is the array that your PYTHONPATH will be transformed into on interpreter startup.


回答 2

如果您sys.path.append('dir/to/path')不加检查就放了它,则可以在中生成一个长列表sys.path。为此,我建议这样做:

import sys
import os # if you want this directory

try:
    sys.path.index('/dir/path') # Or os.getcwd() for this directory
except ValueError:
    sys.path.append('/dir/path') # Or os.getcwd() for this directory

If you put sys.path.append('dir/to/path') without check it is already added, you could generate a long list in sys.path. For that, I recommend this:

import sys
import os # if you want this directory

try:
    sys.path.index('/dir/path') # Or os.getcwd() for this directory
except ValueError:
    sys.path.append('/dir/path') # Or os.getcwd() for this directory

回答 3

PYTHONPATH结尾于sys.path,您可以在运行时进行修改。

import sys
sys.path += ["whatever"]

PYTHONPATH ends up in sys.path, which you can modify at runtime.

import sys
sys.path += ["whatever"]

回答 4

您可以通过设置PYTHONPATHos.environ['PATHPYTHON']=/some/path然后需要调用os.system('python')以重新启动python shell,以使新添加的路径生效。

you can set PYTHONPATH, by os.environ['PATHPYTHON']=/some/path, then you need to call os.system('python') to restart the python shell to make the newly added path effective.


回答 5

我的Linux也可以:

import sys
sys.path.extend(["/path/to/dotpy/file/"])

I linux this works too:

import sys
sys.path.extend(["/path/to/dotpy/file/"])

使用app.yaml将环境变量安全地存储在GAE中

问题:使用app.yaml将环境变量安全地存储在GAE中

我需要将API密钥和其他敏感信息存储app.yaml为环境变量,以便在GAE上进行部署。问题是如果我推app.yaml送到GitHub,此信息将公开(不好)。我不想将信息存储在数据存储中,因为它不适合该项目。相反,我想换出.gitignore应用程序每次部署中列出的文件中的值。

这是我的app.yaml文件:

application: myapp
version: 3 
runtime: python27
api_version: 1
threadsafe: true

libraries:
- name: webapp2
  version: latest
- name: jinja2
  version: latest

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.application  
  login: required
  secure: always
# auth_fail_action: unauthorized

env_variables:
  CLIENT_ID: ${CLIENT_ID}
  CLIENT_SECRET: ${CLIENT_SECRET}
  ORG: ${ORG}
  ACCESS_TOKEN: ${ACCESS_TOKEN}
  SESSION_SECRET: ${SESSION_SECRET}

有任何想法吗?

I need to store API keys and other sensitive information in app.yaml as environment variables for deployment on GAE. The issue with this is that if I push app.yaml to GitHub, this information becomes public (not good). I don’t want to store the info in a datastore as it does not suit the project. Rather, I’d like to swap out the values from a file that is listed in .gitignore on each deployment of the app.

Here is my app.yaml file:

application: myapp
version: 3 
runtime: python27
api_version: 1
threadsafe: true

libraries:
- name: webapp2
  version: latest
- name: jinja2
  version: latest

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.application  
  login: required
  secure: always
# auth_fail_action: unauthorized

env_variables:
  CLIENT_ID: ${CLIENT_ID}
  CLIENT_SECRET: ${CLIENT_SECRET}
  ORG: ${ORG}
  ACCESS_TOKEN: ${ACCESS_TOKEN}
  SESSION_SECRET: ${SESSION_SECRET}

Any ideas?


回答 0

如果是敏感数据,则不应将其存储在源代码中,因为它将被检查到源代码管理中。错误的人(组织内部或外部)可能会在此处找到它。另外,您的开发环境可能会使用与生产环境不同的配置值。如果这些值存储在代码中,则您将不得不在开发和生产中运行不同的代码,这是很麻烦的做法。

在我的项目中,我使用此类将配置数据放入数据存储区:

from google.appengine.ext import ndb

class Settings(ndb.Model):
  name = ndb.StringProperty()
  value = ndb.StringProperty()

  @staticmethod
  def get(name):
    NOT_SET_VALUE = "NOT SET"
    retval = Settings.query(Settings.name == name).get()
    if not retval:
      retval = Settings()
      retval.name = name
      retval.value = NOT_SET_VALUE
      retval.put()
    if retval.value == NOT_SET_VALUE:
      raise Exception(('Setting %s not found in the database. A placeholder ' +
        'record has been created. Go to the Developers Console for your app ' +
        'in App Engine, look up the Settings record with name=%s and enter ' +
        'its value in that record\'s value field.') % (name, name))
    return retval.value

您的应用程序将这样做以获取价值:

API_KEY = Settings.get('API_KEY')

如果数据存储中有该键的值,则将获得它。如果没有,将创建一个占位符记录并引发异常。该异常将提醒您转到开发人员控制台并更新占位符记录。

我发现这消除了对设置配置值的猜测。如果不确定要设置哪些配置值,只需运行代码,它将告诉您!

上面的代码使用了ndb库,该库使用了memcache和后台的数据存储,因此速度很快。


更新:

jelder询问如何在App Engine控制台中找到数据存储区值并进行设置。方法如下:

  1. 前往https://console.cloud.google.com/datastore/

  2. 如果尚未选择项目,请在页面顶部选择它。

  3. 种类下拉框中,选择设置

  4. 如果您运行上面的代码,您的密钥将会显示。它们都将具有值NOT SET。单击每个并设置其值。

希望这可以帮助!

If it’s sensitive data, you should not store it in source code as it will be checked into source control. The wrong people (inside or outside your organization) may find it there. Also, your development environment probably uses different config values from your production environment. If these values are stored in code, you will have to run different code in development and production, which is messy and bad practice.

In my projects, I put config data in the datastore using this class:

from google.appengine.ext import ndb

class Settings(ndb.Model):
  name = ndb.StringProperty()
  value = ndb.StringProperty()

  @staticmethod
  def get(name):
    NOT_SET_VALUE = "NOT SET"
    retval = Settings.query(Settings.name == name).get()
    if not retval:
      retval = Settings()
      retval.name = name
      retval.value = NOT_SET_VALUE
      retval.put()
    if retval.value == NOT_SET_VALUE:
      raise Exception(('Setting %s not found in the database. A placeholder ' +
        'record has been created. Go to the Developers Console for your app ' +
        'in App Engine, look up the Settings record with name=%s and enter ' +
        'its value in that record\'s value field.') % (name, name))
    return retval.value

Your application would do this to get a value:

API_KEY = Settings.get('API_KEY')

If there is a value for that key in the datastore, you will get it. If there isn’t, a placeholder record will be created and an exception will be thrown. The exception will remind you to go to the Developers Console and update the placeholder record.

I find this takes the guessing out of setting config values. If you are unsure of what config values to set, just run the code and it will tell you!

The code above uses the ndb library which uses memcache and the datastore under the hood, so it’s fast.


Update:

jelder asked for how to find the Datastore values in the App Engine console and set them. Here is how:

  1. Go to https://console.cloud.google.com/datastore/

  2. Select your project at the top of the page if it’s not already selected.

  3. In the Kind dropdown box, select Settings.

  4. If you ran the code above, your keys will show up. They will all have the value NOT SET. Click each one and set its value.

Hope this helps!


回答 1

此解决方案很简单,但可能不适合所有不同的团队。

首先,将环境变量放入env_variables.yaml中,例如,

env_variables:
  SECRET: 'my_secret'

然后,将其包含env_variables.yamlapp.yaml

includes:
  - env_variables.yaml

最后,将添加env_variables.yaml.gitignore,以使秘密变量在存储库中不存在。

在这种情况下,env_variables.yaml需要在部署管理器之间共享。

This solution is simple but may not suit all different teams.

First, put the environment variables in an env_variables.yaml, e.g.,

env_variables:
  SECRET: 'my_secret'

Then, include this env_variables.yaml in the app.yaml

includes:
  - env_variables.yaml

Finally, add the env_variables.yaml to .gitignore, so that the secret variables won’t exist in the repository.

In this case, the env_variables.yaml needs to be shared among the deployment managers.


回答 2

我的方法是将客户端机密存储在App Engine应用本身中。客户端机密既不在源代码控制中,也不在任何本地计算机上。这样的好处是,任何 App Engine合作者都可以部署代码更改,而不必担心客户端机密。

我将客户端机密直接存储在数据存储区中,并使用Memcache改善了访问机密的延迟。数据存储区实体仅需要创建一次,并将在以后的部署中保持不变。当然,可以随时使用App Engine控制台更新这些实体。

有两种方法可以执行一次性实体创建:

  • 使用App Engine 远程API交互式外壳程序创建实体。
  • 创建一个仅管理员处理程序,该处理程序将使用伪值初始化实体。手动调用此管理处理程序,然后使用App Engine控制台使用生产客户端密码更新实体。

My approach is to store client secrets only within the App Engine app itself. The client secrets are neither in source control nor on any local computers. This has the benefit that any App Engine collaborator can deploy code changes without having to worry about the client secrets.

I store client secrets directly in Datastore and use Memcache for improved latency accessing the secrets. The Datastore entities only need to be created once and will persist across future deploys. of course the App Engine console can be used to update these entities at any time.

There are two options to perform the one-time entity creation:

  • Use the App Engine Remote API interactive shell to create the entities.
  • Create an Admin only handler that will initialize the entities with dummy values. Manually invoke this admin handler, then use the App Engine console to update the entities with the production client secrets.

回答 3

最好的方法是将密钥存储在client_secrets.json文件中,并通过在.gitignore文件中列出密钥,将其从上传到git中排除。如果您在不同环境下使用不同的密钥,则可以使用app_identity api来确定应用程序ID是什么,并进行适当加载。

这里有一个相当全面的示例-> https://developers.google.com/api-client-library/python/guide/aaa_client_secrets

这是一些示例代码:

# declare your app ids as globals ...
APPID_LIVE = 'awesomeapp'
APPID_DEV = 'awesomeapp-dev'
APPID_PILOT = 'awesomeapp-pilot'

# create a dictionary mapping the app_ids to the filepaths ...
client_secrets_map = {APPID_LIVE:'client_secrets_live.json',
                      APPID_DEV:'client_secrets_dev.json',
                      APPID_PILOT:'client_secrets_pilot.json'}

# get the filename based on the current app_id ...
client_secrets_filename = client_secrets_map.get(
    app_identity.get_application_id(),
    APPID_DEV # fall back to dev
    )

# use the filename to construct the flow ...
flow = flow_from_clientsecrets(filename=client_secrets_filename,
                               scope=scope,
                               redirect_uri=redirect_uri)

# or, you could load up the json file manually if you need more control ...
f = open(client_secrets_filename, 'r')
client_secrets = json.loads(f.read())
f.close()

Best way to do it, is store the keys in a client_secrets.json file, and exclude that from being uploaded to git by listing it in your .gitignore file. If you have different keys for different environments, you can use app_identity api to determine what the app id is, and load appropriately.

There is a fairly comprehensive example here -> https://developers.google.com/api-client-library/python/guide/aaa_client_secrets.

Here’s some example code:

# declare your app ids as globals ...
APPID_LIVE = 'awesomeapp'
APPID_DEV = 'awesomeapp-dev'
APPID_PILOT = 'awesomeapp-pilot'

# create a dictionary mapping the app_ids to the filepaths ...
client_secrets_map = {APPID_LIVE:'client_secrets_live.json',
                      APPID_DEV:'client_secrets_dev.json',
                      APPID_PILOT:'client_secrets_pilot.json'}

# get the filename based on the current app_id ...
client_secrets_filename = client_secrets_map.get(
    app_identity.get_application_id(),
    APPID_DEV # fall back to dev
    )

# use the filename to construct the flow ...
flow = flow_from_clientsecrets(filename=client_secrets_filename,
                               scope=scope,
                               redirect_uri=redirect_uri)

# or, you could load up the json file manually if you need more control ...
f = open(client_secrets_filename, 'r')
client_secrets = json.loads(f.read())
f.close()

回答 4

发布时不存在此功能,但对于在这里偶然发现的其他人,Google现在提供一项称为Secret Manager的服务

这是一个简单的REST服务(当然,其中包含SDK)将您的机密存储在Google云平台上的安全位置。与Data Store相比,这是一种更好的方法,需要额外的步骤来查看存储的机密并具有更细粒度的权限模型-如果需要,您可以针对项目的不同方面以不同的方式保护单个机密。

它提供版本控制,因此您可以相对轻松地处理密码更改,以及强大的查询和管理层,使您能够在必要时在运行时发现和创建机密信息。

Python SDK

用法示例:

from google.cloud import secretmanager_v1beta1 as secretmanager

secret_id = 'my_secret_key'
project_id = 'my_project'
version = 1    # use the management tools to determine version at runtime

client = secretmanager.SecretManagerServiceClient()

secret_path = client.secret_verion_path(project_id, secret_id, version)
response = client.access_secret_version(secret_path)
password_string = response.payload.data.decode('UTF-8')

# use password_string -- set up database connection, call third party service, whatever

This didn’t exist when you posted, but for anyone else who stumbles in here, Google now offers a service called Secret Manager.

It’s a simple REST service (with SDKs wrapping it, of course) to store your secrets in a secure location on google cloud platform. This is a better approach than Data Store, requiring extra steps to see the stored secrets and having a finer-grained permission model — you can secure individual secrets differently for different aspects of your project, if you need to.

It offers versioning, so you can handle password changes with relative ease, as well as a robust query and management layer enabling you to discover and create secrets at runtime, if necessary.

Python SDK

Example usage:

from google.cloud import secretmanager_v1beta1 as secretmanager

secret_id = 'my_secret_key'
project_id = 'my_project'
version = 1    # use the management tools to determine version at runtime

client = secretmanager.SecretManagerServiceClient()

secret_path = client.secret_verion_path(project_id, secret_id, version)
response = client.access_secret_version(secret_path)
password_string = response.payload.data.decode('UTF-8')

# use password_string -- set up database connection, call third party service, whatever

回答 5

此解决方案依赖于已弃用的appcfg.py

将应用程序部署到GAE时,可以使用appcfg.py的-E命令行选项设置环境变量(appcfg.py更新)

$ appcfg.py
...
-E NAME:VALUE, --env_variable=NAME:VALUE
                    Set an environment variable, potentially overriding an
                    env_variable value from app.yaml file (flag may be
                    repeated to set multiple variables).
...

This solution relies on the deprecated appcfg.py

You can use the -E command line option of appcfg.py to setup the environment variables when you deploy your app to GAE (appcfg.py update)

$ appcfg.py
...
-E NAME:VALUE, --env_variable=NAME:VALUE
                    Set an environment variable, potentially overriding an
                    env_variable value from app.yaml file (flag may be
                    repeated to set multiple variables).
...

回答 6

大多数答案已过时。实际上,现在使用Google Cloud Datastore有点不同。https://cloud.google.com/python/getting-started/using-cloud-datastore

这是一个例子:

from google.cloud import datastore
client = datastore.Client()
datastore_entity = client.get(client.key('settings', 'TWITTER_APP_KEY'))
connection_string_prod = datastore_entity.get('value')

假设实体名称为“ TWITTER_APP_KEY”,种类为“设置”,“值”为TWITTER_APP_KEY实体的属性。

Most answers are outdated. Using google cloud datastore is actually a bit different right now. https://cloud.google.com/python/getting-started/using-cloud-datastore

Here’s an example:

from google.cloud import datastore
client = datastore.Client()
datastore_entity = client.get(client.key('settings', 'TWITTER_APP_KEY'))
connection_string_prod = datastore_entity.get('value')

This assumes the entity name is ‘TWITTER_APP_KEY’, the kind is ‘settings’, and ‘value’ is a property of the TWITTER_APP_KEY entity.


回答 7

听起来您可以采取一些方法。我们有一个类似的问题,请执行以下操作(以适合您的用例):

  • 创建一个存储任何动态app.yaml值的文件,并将其放置在构建环境中的安全服务器上。如果您确实偏执,则可以非对称地加密值。如果您需要版本控制/动态拉取,甚至可以将其保存在专用回购中,或者仅使用shell脚本将其复制/从适当的地方拉出。
  • 在部署脚本期间从git中提取
  • 在git pull之后,通过使用yaml库在纯python中读写来修改app.yaml

最简单的方法是使用持续集成服务器,例如HudsonBambooJenkins。只需添加一些插件,脚本步骤或工作流程即可完成我提到的所有上述项目。例如,您可以传入在Bamboo本身中配置的环境变量。

总之,在您只能访问的环境中,只需在构建过程中输入值即可。如果您尚未使构建自动化,则应该这样做。

另一个选项就是您所说的内容,将其放入数据库中。如果您不这样做的原因是操作太慢,则只需将值作为第二层缓存推送到内存缓存中,然后将值作为第一层缓存固定到实例即可。如果值可以更改并且您需要在不重新启动实例的情况下更新实例,则只需保留一个散列即可检查它们何时更改,或者在您进行某些更改后以某种方式触发它。应该的。

It sounds like you can do a few approaches. We have a similar issue and do the following (adapted to your use-case):

  • Create a file that stores any dynamic app.yaml values and place it on a secure server in your build environment. If you are really paranoid, you can asymmetrically encrypt the values. You can even keep this in a private repo if you need version control/dynamic pulling, or just use a shells script to copy it/pull it from the appropriate place.
  • Pull from git during the deployment script
  • After the git pull, modify the app.yaml by reading and writing it in pure python using a yaml library

The easiest way to do this is to use a continuous integration server such as Hudson, Bamboo, or Jenkins. Simply add some plug-in, script step, or workflow that does all the above items I mentioned. You can pass in environment variables that are configured in Bamboo itself for example.

In summary, just push in the values during your build process in an environment you only have access to. If you aren’t already automating your builds, you should be.

Another option option is what you said, put it in the database. If your reason for not doing that is that things are too slow, simply push the values into memcache as a 2nd layer cache, and pin the values to the instances as a first-layer cache. If the values can change and you need to update the instances without rebooting them, just keep a hash you can check to know when they change or trigger it somehow when something you do changes the values. That should be it.


回答 8

您应该使用google kms加密变量,并将其嵌入到源代码中。(https://cloud.google.com/kms/

echo -n the-twitter-app-key | gcloud kms encrypt \
> --project my-project \
> --location us-central1 \
> --keyring THEKEYRING \
> --key THECRYPTOKEY \
> --plaintext-file - \
> --ciphertext-file - \
> | base64

将加扰后的值(加密并以base64编码)放入您的环境变量(在yaml文件中)。

一些Python式代码可帮助您开始解密。

kms_client = kms_v1.KeyManagementServiceClient()
name = kms_client.crypto_key_path_path("project", "global", "THEKEYRING", "THECRYPTOKEY")

twitter_app_key = kms_client.decrypt(name, base64.b64decode(os.environ.get("TWITTER_APP_KEY"))).plaintext

You should encrypt the variables with google kms and embed it in your source code. (https://cloud.google.com/kms/)

echo -n the-twitter-app-key | gcloud kms encrypt \
> --project my-project \
> --location us-central1 \
> --keyring THEKEYRING \
> --key THECRYPTOKEY \
> --plaintext-file - \
> --ciphertext-file - \
> | base64

put the scrambled (encrypted and base64 encoded) value into your environment variable (in yaml file).

Some pythonish code to get you started on decrypting.

kms_client = kms_v1.KeyManagementServiceClient()
name = kms_client.crypto_key_path_path("project", "global", "THEKEYRING", "THECRYPTOKEY")

twitter_app_key = kms_client.decrypt(name, base64.b64decode(os.environ.get("TWITTER_APP_KEY"))).plaintext

回答 9

@Jason F 基于使用Google数据存储的答案很接近,但是基于库docs上的示例用法,代码有些过时了。这是对我有用的代码片段:

from google.cloud import datastore

client = datastore.Client('<your project id>')
key = client.key('<kind e.g settings>', '<entity name>') # note: entity name not property
# get by key for this entity
result = client.get(key)
print(result) # prints all the properties ( a dict). index a specific value like result['MY_SECRET_KEY'])

部分受此中篇文章的启发

@Jason F’s answer based on using Google Datastore is close, but the code is a bit outdated based on the sample usage on the library docs. Here’s the snippet that worked for me:

from google.cloud import datastore

client = datastore.Client('<your project id>')
key = client.key('<kind e.g settings>', '<entity name>') # note: entity name not property
# get by key for this entity
result = client.get(key)
print(result) # prints all the properties ( a dict). index a specific value like result['MY_SECRET_KEY'])

Partly inspired by this Medium post


回答 10

只是想说明一下我是如何在javascript / nodejs中解决此问题的。对于本地开发,我使用了“ dotenv” npm软件包,该软件包将环境变量从.env文件加载到process.env中。当我开始使用GAE时,我了解到需要在“ app.yaml”文件中设置环境变量。好吧,我不想将’dotenv’用于本地开发,而不想将’app.yaml’用于GAE(并在两个文件之间复制我的环境变量),所以我编写了一个小脚本,将app.yaml环境变量加载到进程中.env,用于本地开发。希望这对某人有帮助:

yaml_env.js:

(function () {
    const yaml = require('js-yaml');
    const fs = require('fs');
    const isObject = require('lodash.isobject')

    var doc = yaml.safeLoad(
        fs.readFileSync('app.yaml', 'utf8'), 
        { json: true }
    );

    // The .env file will take precedence over the settings the app.yaml file
    // which allows me to override stuff in app.yaml (the database connection string (DATABASE_URL), for example)
    // This is optional of course. If you don't use dotenv then remove this line:
    require('dotenv/config');

    if(isObject(doc) && isObject(doc.env_variables)) {
        Object.keys(doc.env_variables).forEach(function (key) {
            // Dont set environment with the yaml file value if it's already set
            process.env[key] = process.env[key] || doc.env_variables[key]
        })
    }
})()

现在,尽早将此代码包含在您的代码中,您已完成:

require('../yaml_env')

Just wanted to note how I solved this problem in javascript/nodejs. For local development I used the ‘dotenv’ npm package which loads environment variables from a .env file into process.env. When I started using GAE I learned that environment variables need to be set in a ‘app.yaml’ file. Well, I didn’t want to use ‘dotenv’ for local development and ‘app.yaml’ for GAE (and duplicate my environment variables between the two files), so I wrote a little script that loads app.yaml environment variables into process.env, for local development. Hope this helps someone:

yaml_env.js:

(function () {
    const yaml = require('js-yaml');
    const fs = require('fs');
    const isObject = require('lodash.isobject')

    var doc = yaml.safeLoad(
        fs.readFileSync('app.yaml', 'utf8'), 
        { json: true }
    );

    // The .env file will take precedence over the settings the app.yaml file
    // which allows me to override stuff in app.yaml (the database connection string (DATABASE_URL), for example)
    // This is optional of course. If you don't use dotenv then remove this line:
    require('dotenv/config');

    if(isObject(doc) && isObject(doc.env_variables)) {
        Object.keys(doc.env_variables).forEach(function (key) {
            // Dont set environment with the yaml file value if it's already set
            process.env[key] = process.env[key] || doc.env_variables[key]
        })
    }
})()

Now include this file as early as possible in your code, and you’re done:

require('../yaml_env')

回答 11

扩展马丁的答案

from google.appengine.ext import ndb

class Settings(ndb.Model):
    """
    Get sensitive data setting from DataStore.

    key:String -> value:String
    key:String -> Exception

    Thanks to: Martin Omander @ Stackoverflow
    https://stackoverflow.com/a/35261091/1463812
    """
    name = ndb.StringProperty()
    value = ndb.StringProperty()

    @staticmethod
    def get(name):
        retval = Settings.query(Settings.name == name).get()
        if not retval:
            raise Exception(('Setting %s not found in the database. A placeholder ' +
                             'record has been created. Go to the Developers Console for your app ' +
                             'in App Engine, look up the Settings record with name=%s and enter ' +
                             'its value in that record\'s value field.') % (name, name))
        return retval.value

    @staticmethod
    def set(name, value):
        exists = Settings.query(Settings.name == name).get()
        if not exists:
            s = Settings(name=name, value=value)
            s.put()
        else:
            exists.value = value
            exists.put()

        return True

Extending Martin’s answer

from google.appengine.ext import ndb

class Settings(ndb.Model):
    """
    Get sensitive data setting from DataStore.

    key:String -> value:String
    key:String -> Exception

    Thanks to: Martin Omander @ Stackoverflow
    https://stackoverflow.com/a/35261091/1463812
    """
    name = ndb.StringProperty()
    value = ndb.StringProperty()

    @staticmethod
    def get(name):
        retval = Settings.query(Settings.name == name).get()
        if not retval:
            raise Exception(('Setting %s not found in the database. A placeholder ' +
                             'record has been created. Go to the Developers Console for your app ' +
                             'in App Engine, look up the Settings record with name=%s and enter ' +
                             'its value in that record\'s value field.') % (name, name))
        return retval.value

    @staticmethod
    def set(name, value):
        exists = Settings.query(Settings.name == name).get()
        if not exists:
            s = Settings(name=name, value=value)
            s.put()
        else:
            exists.value = value
            exists.put()

        return True

回答 12

有一个名为gae_env的pypi软件包,可让您将Appengine环境变量保存在Cloud Datastore中。在后台,它还使用Memcache,因此其速度很快

用法:

import gae_env

API_KEY = gae_env.get('API_KEY')

如果数据存储中有该键的值,则将其返回。如果没有,__NOT_SET__将创建一个占位符记录并ValueNotSetError抛出一个。该异常将提醒您转到开发人员控制台并更新占位符记录。


与Martin的答案类似,这是如何更新数据存储区中键的值:

  1. 转到开发人员控制台中的“ 数据存储”部分

  2. 如果尚未选择项目,请在页面顶部选择它。

  3. 在“ 种类”下拉框中,选择GaeEnvSettings

  4. 引发异常的键将具有价值__NOT_SET__


转到软件包的GitHub页面以获取有关用法/配置的更多信息

There is a pypi package called gae_env that allows you to save appengine environment variables in Cloud Datastore. Under the hood, it also uses Memcache so its fast

Usage:

import gae_env

API_KEY = gae_env.get('API_KEY')

If there is a value for that key in the datastore, it will be returned. If there isn’t, a placeholder record __NOT_SET__ will be created and a ValueNotSetError will be thrown. The exception will remind you to go to the Developers Console and update the placeholder record.


Similar to Martin’s answer, here is how to update the value for the key in Datastore:

  1. Go to Datastore Section in the developers console

  2. Select your project at the top of the page if it’s not already selected.

  3. In the Kind dropdown box, select GaeEnvSettings.

  4. Keys for which an exception was raised will have value __NOT_SET__.


Go to the package’s GitHub page for more info on usage/configuration


解析配置文件,环境和命令行参数,以获取单个选项集合

问题:解析配置文件,环境和命令行参数,以获取单个选项集合

Python的标准库具有用于配置文件解析configparser),环境变量读取os.environ)和命令行参数解析argparse)的模块。我想编写一个可以完成所有这些任务的程序,并且:

  • 具有一系列的选项值

    • 默认选项值,被覆盖
    • 配置文件选项,被覆盖
    • 环境变量,被覆盖
    • 命令行选项。
  • 允许在命令行上使用例如指定一个或多个配置文件位置--config-file foo.conf,并读取该位置(代替或添加到常规配置文件中)。这仍然必须遵循上述级联。

  • 允许在单个位置定义选项,以确定配置文件和命令行的解析行为。

  • 将已解析的选项统一为一个选项值集合,供程序的其余部分访问,而无需关心它们的来源。

我需要的所有内容显然都在Python标准库中,但它们不能一起正常工作。

如何以最小的Python标准库偏差实现此目标?

Python’s standard library has modules for configuration file parsing (configparser), environment variable reading (os.environ), and command-line argument parsing (argparse). I want to write a program that does all those, and also:

  • Has a cascade of option values:

    • default option values, overridden by
    • config file options, overridden by
    • environment variables, overridden by
    • command-line options.
  • Allows one or more configuration file locations specified on the command line with e.g. --config-file foo.conf, and reads that (either instead of, or additional to, the usual configuration file). This must still obey the above cascade.

  • Allows option definitions in a single place to determine the parsing behaviour for configuration files and the command line.

  • Unifies the parsed options into a single collection of option values for the rest of the program to access without caring where they came from.

Everything I need is apparently in the Python standard library, but they don’t work together smoothly.

How can I achieve this with minimum deviation from the Python standard library?


回答 0

只要您对看起来像命令行的配置文件感到满意,argparse模块就不会让您感到困惑。(我认为这是一个优势,因为用户只需要学习一种语法即可。)例如,将fromfile_prefix_chars设置为@,可以做到,

my_prog --foo=bar

相当于

my_prog @baz.conf

如果@baz.conf是,

--foo
bar

您甚至可以foo.conf通过修改来自动寻找代码argv

if os.path.exists('foo.conf'):
    argv = ['@foo.conf'] + argv
args = argparser.parse_args(argv)

通过配置ArgumentParser的子类并添加convert_arg_line_to_args方法,可以修改这些配置文件的格式。

The argparse module makes this not nuts, as long as you’re happy with a config file that looks like command line. (I think this is an advantage, because users will only have to learn one syntax.) Setting fromfile_prefix_chars to, for example, @, makes it so that,

my_prog --foo=bar

is equivalent to

my_prog @baz.conf

if @baz.conf is,

--foo
bar

You can even have your code look for foo.conf automatically by modifying argv

if os.path.exists('foo.conf'):
    argv = ['@foo.conf'] + argv
args = argparser.parse_args(argv)

The format of these configuration files is modifiable by making a subclass of ArgumentParser and adding a convert_arg_line_to_args method.


回答 1

更新: 我终于把它放在pypi上了。通过以下方式安装最新版本:

   pip install configargparser

完整的帮助和说明在这里

原始帖子

这是我一起砍的一些东西。随意在评论中提出改进建议/错误报告:

import argparse
import ConfigParser
import os

def _identity(x):
    return x

_SENTINEL = object()


class AddConfigFile(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        # I can never remember if `values` is a list all the time or if it
        # can be a scalar string; this takes care of both.
        if isinstance(values,basestring):
            parser.config_files.append(values)
        else:
            parser.config_files.extend(values)


class ArgumentConfigEnvParser(argparse.ArgumentParser):
    def __init__(self,*args,**kwargs):
        """
        Added 2 new keyword arguments to the ArgumentParser constructor:

           config --> List of filenames to parse for config goodness
           default_section --> name of the default section in the config file
        """
        self.config_files = kwargs.pop('config',[])  #Must be a list
        self.default_section = kwargs.pop('default_section','MAIN')
        self._action_defaults = {}
        argparse.ArgumentParser.__init__(self,*args,**kwargs)


    def add_argument(self,*args,**kwargs):
        """
        Works like `ArgumentParser.add_argument`, except that we've added an action:

           config: add a config file to the parser

        This also adds the ability to specify which section of the config file to pull the 
        data from, via the `section` keyword.  This relies on the (undocumented) fact that
        `ArgumentParser.add_argument` actually returns the `Action` object that it creates.
        We need this to reliably get `dest` (although we could probably write a simple
        function to do this for us).
        """

        if 'action' in kwargs and kwargs['action'] == 'config':
            kwargs['action'] = AddConfigFile
            kwargs['default'] = argparse.SUPPRESS

        # argparse won't know what to do with the section, so 
        # we'll pop it out and add it back in later.
        #
        # We also have to prevent argparse from doing any type conversion,
        # which is done explicitly in parse_known_args.  
        #
        # This way, we can reliably check whether argparse has replaced the default.
        #
        section = kwargs.pop('section', self.default_section)
        type = kwargs.pop('type', _identity)
        default = kwargs.pop('default', _SENTINEL)

        if default is not argparse.SUPPRESS:
            kwargs.update(default=_SENTINEL)
        else:  
            kwargs.update(default=argparse.SUPPRESS)

        action = argparse.ArgumentParser.add_argument(self,*args,**kwargs)
        kwargs.update(section=section, type=type, default=default)
        self._action_defaults[action.dest] = (args,kwargs)
        return action

    def parse_known_args(self,args=None, namespace=None):
        # `parse_args` calls `parse_known_args`, so we should be okay with this...
        ns, argv = argparse.ArgumentParser.parse_known_args(self, args=args, namespace=namespace)
        config_parser = ConfigParser.SafeConfigParser()
        config_files = [os.path.expanduser(os.path.expandvars(x)) for x in self.config_files]
        config_parser.read(config_files)

        for dest,(args,init_dict) in self._action_defaults.items():
            type_converter = init_dict['type']
            default = init_dict['default']
            obj = default

            if getattr(ns,dest,_SENTINEL) is not _SENTINEL: # found on command line
                obj = getattr(ns,dest)
            else: # not found on commandline
                try:  # get from config file
                    obj = config_parser.get(init_dict['section'],dest)
                except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): # Nope, not in config file
                    try: # get from environment
                        obj = os.environ[dest.upper()]
                    except KeyError:
                        pass

            if obj is _SENTINEL:
                setattr(ns,dest,None)
            elif obj is argparse.SUPPRESS:
                pass
            else:
                setattr(ns,dest,type_converter(obj))

        return ns, argv


if __name__ == '__main__':
    fake_config = """
[MAIN]
foo:bar
bar:1
"""
    with open('_config.file','w') as fout:
        fout.write(fake_config)

    parser = ArgumentConfigEnvParser()
    parser.add_argument('--config-file', action='config', help="location of config file")
    parser.add_argument('--foo', type=str, action='store', default="grape", help="don't know what foo does ...")
    parser.add_argument('--bar', type=int, default=7, action='store', help="This is an integer (I hope)")
    parser.add_argument('--baz', type=float, action='store', help="This is an float(I hope)")
    parser.add_argument('--qux', type=int, default='6', action='store', help="this is another int")
    ns = parser.parse_args([])

    parser_defaults = {'foo':"grape",'bar':7,'baz':None,'qux':6}
    config_defaults = {'foo':'bar','bar':1}
    env_defaults = {"baz":3.14159}

    # This should be the defaults we gave the parser
    print ns
    assert ns.__dict__ == parser_defaults

    # This should be the defaults we gave the parser + config defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    os.environ['BAZ'] = "3.14159"

    # This should be the parser defaults + config defaults + env_defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    # This should be the parser defaults + config defaults + env_defaults + commandline
    commandline = {'foo':'3','qux':4} 
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    d.update(commandline)
    ns = parser.parse_args(['--config-file','_config.file','--foo=3','--qux=4'])
    print ns
    assert ns.__dict__ == d

    os.remove('_config.file')

去做

此实现仍未完成。以下是部分待办事项清单:

符合记录的行为

  • (简单)编写一个destargsin 弄清楚的函数add_argument,而不是依赖于Action对象
  • (平凡的)编写一个parse_args使用的函数parse_known_args。(例如,parse_argscpython实现中复制以确保调用它parse_known_args。)

不太容易的东西

我还没有尝试过。它不太可能(但仍然有可能!)能够正常工作……

  • (很难?)互斥
  • (很难?)参数组 (如果实现,这些组应section在配置文件中获得一个。)
  • (硬吗?)子命令 (子命令也应section在配置文件中得到一个。)

UPDATE: I finally got around to putting this on pypi. Install latest version via:

   pip install configargparser

Full help and instructions are here.

Original post

Here’s a little something that I hacked together. Feel free suggest improvements/bug-reports in the comments:

import argparse
import ConfigParser
import os

def _identity(x):
    return x

_SENTINEL = object()


class AddConfigFile(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        # I can never remember if `values` is a list all the time or if it
        # can be a scalar string; this takes care of both.
        if isinstance(values,basestring):
            parser.config_files.append(values)
        else:
            parser.config_files.extend(values)


class ArgumentConfigEnvParser(argparse.ArgumentParser):
    def __init__(self,*args,**kwargs):
        """
        Added 2 new keyword arguments to the ArgumentParser constructor:

           config --> List of filenames to parse for config goodness
           default_section --> name of the default section in the config file
        """
        self.config_files = kwargs.pop('config',[])  #Must be a list
        self.default_section = kwargs.pop('default_section','MAIN')
        self._action_defaults = {}
        argparse.ArgumentParser.__init__(self,*args,**kwargs)


    def add_argument(self,*args,**kwargs):
        """
        Works like `ArgumentParser.add_argument`, except that we've added an action:

           config: add a config file to the parser

        This also adds the ability to specify which section of the config file to pull the 
        data from, via the `section` keyword.  This relies on the (undocumented) fact that
        `ArgumentParser.add_argument` actually returns the `Action` object that it creates.
        We need this to reliably get `dest` (although we could probably write a simple
        function to do this for us).
        """

        if 'action' in kwargs and kwargs['action'] == 'config':
            kwargs['action'] = AddConfigFile
            kwargs['default'] = argparse.SUPPRESS

        # argparse won't know what to do with the section, so 
        # we'll pop it out and add it back in later.
        #
        # We also have to prevent argparse from doing any type conversion,
        # which is done explicitly in parse_known_args.  
        #
        # This way, we can reliably check whether argparse has replaced the default.
        #
        section = kwargs.pop('section', self.default_section)
        type = kwargs.pop('type', _identity)
        default = kwargs.pop('default', _SENTINEL)

        if default is not argparse.SUPPRESS:
            kwargs.update(default=_SENTINEL)
        else:  
            kwargs.update(default=argparse.SUPPRESS)

        action = argparse.ArgumentParser.add_argument(self,*args,**kwargs)
        kwargs.update(section=section, type=type, default=default)
        self._action_defaults[action.dest] = (args,kwargs)
        return action

    def parse_known_args(self,args=None, namespace=None):
        # `parse_args` calls `parse_known_args`, so we should be okay with this...
        ns, argv = argparse.ArgumentParser.parse_known_args(self, args=args, namespace=namespace)
        config_parser = ConfigParser.SafeConfigParser()
        config_files = [os.path.expanduser(os.path.expandvars(x)) for x in self.config_files]
        config_parser.read(config_files)

        for dest,(args,init_dict) in self._action_defaults.items():
            type_converter = init_dict['type']
            default = init_dict['default']
            obj = default

            if getattr(ns,dest,_SENTINEL) is not _SENTINEL: # found on command line
                obj = getattr(ns,dest)
            else: # not found on commandline
                try:  # get from config file
                    obj = config_parser.get(init_dict['section'],dest)
                except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): # Nope, not in config file
                    try: # get from environment
                        obj = os.environ[dest.upper()]
                    except KeyError:
                        pass

            if obj is _SENTINEL:
                setattr(ns,dest,None)
            elif obj is argparse.SUPPRESS:
                pass
            else:
                setattr(ns,dest,type_converter(obj))

        return ns, argv


if __name__ == '__main__':
    fake_config = """
[MAIN]
foo:bar
bar:1
"""
    with open('_config.file','w') as fout:
        fout.write(fake_config)

    parser = ArgumentConfigEnvParser()
    parser.add_argument('--config-file', action='config', help="location of config file")
    parser.add_argument('--foo', type=str, action='store', default="grape", help="don't know what foo does ...")
    parser.add_argument('--bar', type=int, default=7, action='store', help="This is an integer (I hope)")
    parser.add_argument('--baz', type=float, action='store', help="This is an float(I hope)")
    parser.add_argument('--qux', type=int, default='6', action='store', help="this is another int")
    ns = parser.parse_args([])

    parser_defaults = {'foo':"grape",'bar':7,'baz':None,'qux':6}
    config_defaults = {'foo':'bar','bar':1}
    env_defaults = {"baz":3.14159}

    # This should be the defaults we gave the parser
    print ns
    assert ns.__dict__ == parser_defaults

    # This should be the defaults we gave the parser + config defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    os.environ['BAZ'] = "3.14159"

    # This should be the parser defaults + config defaults + env_defaults
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    ns = parser.parse_args(['--config-file','_config.file'])
    print ns
    assert ns.__dict__ == d

    # This should be the parser defaults + config defaults + env_defaults + commandline
    commandline = {'foo':'3','qux':4} 
    d = parser_defaults.copy()
    d.update(config_defaults)
    d.update(env_defaults)
    d.update(commandline)
    ns = parser.parse_args(['--config-file','_config.file','--foo=3','--qux=4'])
    print ns
    assert ns.__dict__ == d

    os.remove('_config.file')

TODO

This implementation is still incomplete. Here’s a partial TODO list:

Conform to documented behavior

  • (easy) Write a function that figures out dest from args in add_argument, instead of relying on the Action object
  • (trivial) Write a parse_args function which uses parse_known_args. (e.g. copy parse_args from the cpython implementation to guarantee it calls parse_known_args.)

Less Easy Stuff…

I haven’t tried any of this yet. It’s unlikely—but still possible!—that it could just work…


回答 2

有一个库可以做到这一点,叫做configglue

configglue是一个将python的optparse.OptionParser和ConfigParser.ConfigParser粘合在一起的库,因此当您要将相同的选项导出到配置文件和命令行界面时,不必重复自己的操作。

它还支持环境变量。

还有一种叫库ConfigArgParse这是

argparse的直接替代品,允许通过配置文件和/或环境变量设置选项。

您可能对ŁukaszLanga进行的有关PyCon的配置感兴趣- 让他们进行配置!

There’s library that does exactly this called configglue.

configglue is a library that glues together python’s optparse.OptionParser and ConfigParser.ConfigParser, so that you don’t have to repeat yourself when you want to export the same options to a configuration file and a commandline interface.

It also supports environment variables.

There’s also another library called ConfigArgParse which is

A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables.

You might be interested in PyCon talk about configuration by Łukasz Langa – Let Them Configure!


回答 3

虽然我自己没有尝试过,但是有一个ConfigArgParse库,它指出它可以完成您想要的大多数事情:

argparse的直接替代品,允许通过配置文件和/或环境变量设置选项。

While I haven’t tried it by my own, there is ConfigArgParse library which states that it does most of things that you want:

A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables.


回答 4

似乎标准库没有解决这个问题,从而使每个程序员都难以为继configparserargparse并且os.environ以笨拙的方式将它们放在一起。

It seems the standard library doesn’t address this, leaving each programmer to cobble configparser and argparse and os.environ all together in clunky ways.


回答 5

据我所知,Python标准库不提供此功能。我通过编写要使用optparseConfigParser解析命令行和配置文件,并在它们之上提供了抽象层,从而。但是,您需要将此作为单独的依赖项,从您先前的评论看来,这是令人讨厌的。

如果要查看我编写的代码,请访问http://liw.fi/cliapp/。它已集成到我的“命令行应用程序框架”库中,因为这是框架需要执行的大部分工作。

The Python standard library does not provide this, as far as I know. I solved this for myself by writing code to use optparse and ConfigParser to parse the command line and config files, and provide an abstraction layer on top of them. However, you would need this as a separate dependency, which from your earlier comment seems to be unpalatable.

If you want to look at the code I wrote, it’s at http://liw.fi/cliapp/. It’s integrated into my “command line application framework” library, since that’s a large part of what the framework needs to do.


回答 6

最近,我使用“ optparse”进行了类似的尝试。

我使用“ –Store”和“ –Check”命令将其设置为OptonParser的子类。

下面的代码几乎可以涵盖您。您只需要定义自己的“加载”和“存储”方法即可接受/返回字典,因此您已经设置好了。


class SmartParse(optparse.OptionParser):
    def __init__(self,defaults,*args,**kwargs):
        self.smartDefaults=defaults
        optparse.OptionParser.__init__(self,*args,**kwargs)
        fileGroup = optparse.OptionGroup(self,'handle stored defaults')
        fileGroup.add_option(
            '-S','--Store',
            dest='Action',
            action='store_const',const='Store',
            help='store command line settings'
        )
        fileGroup.add_option(
            '-C','--Check',
            dest='Action',
            action='store_const',const='Check',
            help ='check stored settings'
        )
        self.add_option_group(fileGroup)
    def parse_args(self,*args,**kwargs):
        (options,arguments) = optparse.OptionParser.parse_args(self,*args,**kwargs)
        action = options.__dict__.pop('Action')
        if action == 'Check':
            assert all(
                value is None 
                for (key,value) in options.__dict__.iteritems() 
            )
            print 'defaults:',self.smartDefaults
            print 'config:',self.load()
            sys.exit()
        elif action == 'Store':
            self.store(options.__dict__)
            sys.exit()
        else:
            config=self.load()
            commandline=dict(
                [key,val] 
                for (key,val) in options.__dict__.iteritems() 
                if val is not None
            )
            result = {}
            result.update(self.defaults)
            result.update(config)
            result.update(commandline)
            return result,arguments
    def load(self):
        return {}
    def store(self,optionDict):
        print 'Storing:',optionDict

I was tried something like this recently, using “optparse”.

I set it up as a sub-class of OptonParser, with a ‘–Store’ and a ‘–Check’ command.

The code below should pretty much have you covered. You just need to define your own ‘load’ and ‘store’ methods which accept/return dictionaries and you’re prey much set.


class SmartParse(optparse.OptionParser):
    def __init__(self,defaults,*args,**kwargs):
        self.smartDefaults=defaults
        optparse.OptionParser.__init__(self,*args,**kwargs)
        fileGroup = optparse.OptionGroup(self,'handle stored defaults')
        fileGroup.add_option(
            '-S','--Store',
            dest='Action',
            action='store_const',const='Store',
            help='store command line settings'
        )
        fileGroup.add_option(
            '-C','--Check',
            dest='Action',
            action='store_const',const='Check',
            help ='check stored settings'
        )
        self.add_option_group(fileGroup)
    def parse_args(self,*args,**kwargs):
        (options,arguments) = optparse.OptionParser.parse_args(self,*args,**kwargs)
        action = options.__dict__.pop('Action')
        if action == 'Check':
            assert all(
                value is None 
                for (key,value) in options.__dict__.iteritems() 
            )
            print 'defaults:',self.smartDefaults
            print 'config:',self.load()
            sys.exit()
        elif action == 'Store':
            self.store(options.__dict__)
            sys.exit()
        else:
            config=self.load()
            commandline=dict(
                [key,val] 
                for (key,val) in options.__dict__.iteritems() 
                if val is not None
            )
            result = {}
            result.update(self.defaults)
            result.update(config)
            result.update(commandline)
            return result,arguments
    def load(self):
        return {}
    def store(self,optionDict):
        print 'Storing:',optionDict


回答 7

为了满足所有这些要求,我建议编写自己的库,该库同时使用[opt | arg] parse和configparser作为基础功能。

鉴于前两个条件和最后一个要求,我想说你想要:

第一步:执行仅用于–config-file选项的命令行解析器传递。

第二步:解析配置文件。

第三步:使用配置文件pass的输出作为默认值,设置第二个命令行解析器pass。

第三个要求可能意味着您必须设计自己的选项定义系统以公开您关心的optparse和configparser的所有功能,并编写一些管道以在它们之间进行转换。

To hit all those requirements, I would recommend writing your own library that uses both [opt|arg]parse and configparser for the underlying functionality.

Given the first two and the last requirement, I’d say you want:

Step one: Do a command line parser pass that only looks for the –config-file option.

Step two: Parse the config file.

Step three: set up a second command line parser pass using the output of the config file pass as the defaults.

The third requirement likely means you have to design your own option definition system to expose all the functionality of optparse and configparser that you care about, and write some plumbing to do conversions in between.


回答 8

这是我一起学习过的一个模块,可以读取命令行参数,环境设置,ini文件以及密钥环值。它的要点也是可用的。

"""
Configuration Parser

Configurable parser that will parse config files, environment variables,
keyring, and command-line arguments.



Example test.ini file:

    [defaults]
    gini=10

    [app]
    xini = 50

Example test.arg file:

    --xfarg=30

Example test.py file:

    import os
    import sys

    import config


    def main(argv):
        '''Test.'''
        options = [
            config.Option("xpos",
                          help="positional argument",
                          nargs='?',
                          default="all",
                          env="APP_XPOS"),
            config.Option("--xarg",
                          help="optional argument",
                          default=1,
                          type=int,
                          env="APP_XARG"),
            config.Option("--xenv",
                          help="environment argument",
                          default=1,
                          type=int,
                          env="APP_XENV"),
            config.Option("--xfarg",
                          help="@file argument",
                          default=1,
                          type=int,
                          env="APP_XFARG"),
            config.Option("--xini",
                          help="ini argument",
                          default=1,
                          type=int,
                          ini_section="app",
                          env="APP_XINI"),
            config.Option("--gini",
                          help="global ini argument",
                          default=1,
                          type=int,
                          env="APP_GINI"),
            config.Option("--karg",
                          help="secret keyring arg",
                          default=-1,
                          type=int),
        ]
        ini_file_paths = [
            '/etc/default/app.ini',
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'test.ini')
        ]

        # default usage
        conf = config.Config(prog='app', options=options,
                             ini_paths=ini_file_paths)
        conf.parse()
        print conf

        # advanced usage
        cli_args = conf.parse_cli(argv=argv)
        env = conf.parse_env()
        secrets = conf.parse_keyring(namespace="app")
        ini = conf.parse_ini(ini_file_paths)
        sources = {}
        if ini:
            for key, value in ini.iteritems():
                conf[key] = value
                sources[key] = "ini-file"
        if secrets:
            for key, value in secrets.iteritems():
                conf[key] = value
                sources[key] = "keyring"
        if env:
            for key, value in env.iteritems():
                conf[key] = value
                sources[key] = "environment"
        if cli_args:
            for key, value in cli_args.iteritems():
                conf[key] = value
                sources[key] = "command-line"
        print '\n'.join(['%s:\t%s' % (k, v) for k, v in sources.items()])


    if __name__ == "__main__":
        if config.keyring:
            config.keyring.set_password("app", "karg", "13")
        main(sys.argv)

Example results:

    $APP_XENV=10 python test.py api --xarg=2 @test.arg
    <Config xpos=api, gini=1, xenv=10, xini=50, karg=13, xarg=2, xfarg=30>
    xpos:   command-line
    xenv:   environment
    xini:   ini-file
    karg:   keyring
    xarg:   command-line
    xfarg:  command-line


"""
import argparse
import ConfigParser
import copy
import os
import sys

try:
    import keyring
except ImportError:
    keyring = None


class Option(object):
    """Holds a configuration option and the names and locations for it.

    Instantiate options using the same arguments as you would for an
    add_arguments call in argparse. However, you have two additional kwargs
    available:

        env: the name of the environment variable to use for this option
        ini_section: the ini file section to look this value up from
    """

    def __init__(self, *args, **kwargs):
        self.args = args or []
        self.kwargs = kwargs or {}

    def add_argument(self, parser, **override_kwargs):
        """Add an option to a an argparse parser."""
        kwargs = {}
        if self.kwargs:
            kwargs = copy.copy(self.kwargs)
            try:
                del kwargs['env']
            except KeyError:
                pass
            try:
                del kwargs['ini_section']
            except KeyError:
                pass
        kwargs.update(override_kwargs)
        parser.add_argument(*self.args, **kwargs)

    @property
    def type(self):
        """The type of the option.

        Should be a callable to parse options.
        """
        return self.kwargs.get("type", str)

    @property
    def name(self):
        """The name of the option as determined from the args."""
        for arg in self.args:
            if arg.startswith("--"):
                return arg[2:].replace("-", "_")
            elif arg.startswith("-"):
                continue
            else:
                return arg.replace("-", "_")

    @property
    def default(self):
        """The default for the option."""
        return self.kwargs.get("default")


class Config(object):
    """Parses configuration sources."""

    def __init__(self, options=None, ini_paths=None, **parser_kwargs):
        """Initialize with list of options.

        :param ini_paths: optional paths to ini files to look up values from
        :param parser_kwargs: kwargs used to init argparse parsers.
        """
        self._parser_kwargs = parser_kwargs or {}
        self._ini_paths = ini_paths or []
        self._options = copy.copy(options) or []
        self._values = {option.name: option.default
                        for option in self._options}
        self._parser = argparse.ArgumentParser(**parser_kwargs)
        self.pass_thru_args = []

    @property
    def prog(self):
        """Program name."""
        return self._parser.prog

    def __getitem__(self, key):
        return self._values[key]

    def __setitem__(self, key, value):
        self._values[key] = value

    def __delitem__(self, key):
        del self._values[key]

    def __contains__(self, key):
        return key in self._values

    def __iter__(self):
        return iter(self._values)

    def __len__(self):
        return len(self._values)

    def get(self, key, *args):
        """
        Return the value for key if it exists otherwise the default.
        """
        return self._values.get(key, *args)

    def __getattr__(self, attr):
        if attr in self._values:
            return self._values[attr]
        else:
            raise AttributeError("'config' object has no attribute '%s'"
                                 % attr)

    def build_parser(self, options, **override_kwargs):
        """."""
        kwargs = copy.copy(self._parser_kwargs)
        kwargs.update(override_kwargs)
        if 'fromfile_prefix_chars' not in kwargs:
            kwargs['fromfile_prefix_chars'] = '@'
        parser = argparse.ArgumentParser(**kwargs)
        if options:
            for option in options:
                option.add_argument(parser)
        return parser

    def parse_cli(self, argv=None):
        """Parse command-line arguments into values."""
        if not argv:
            argv = sys.argv
        options = []
        for option in self._options:
            temp = Option(*option.args, **option.kwargs)
            temp.kwargs['default'] = argparse.SUPPRESS
            options.append(temp)
        parser = self.build_parser(options=options)
        parsed, extras = parser.parse_known_args(argv[1:])
        if extras:
            valid, pass_thru = self.parse_passthru_args(argv[1:])
            parsed, extras = parser.parse_known_args(valid)
            if extras:
                raise AttributeError("Unrecognized arguments: %s" %
                                     ' ,'.join(extras))
            self.pass_thru_args = pass_thru + extras
        return vars(parsed)

    def parse_env(self):
        results = {}
        for option in self._options:
            env_var = option.kwargs.get('env')
            if env_var and env_var in os.environ:
                value = os.environ[env_var]
                results[option.name] = option.type(value)
        return results

    def get_defaults(self):
        """Use argparse to determine and return dict of defaults."""
        parser = self.build_parser(options=self._options)
        parsed, _ = parser.parse_known_args([])
        return vars(parsed)

    def parse_ini(self, paths=None):
        """Parse config files and return configuration options.

        Expects array of files that are in ini format.
        :param paths: list of paths to files to parse (uses ConfigParse logic).
                      If not supplied, uses the ini_paths value supplied on
                      initialization.
        """
        results = {}
        config = ConfigParser.SafeConfigParser()
        config.read(paths or self._ini_paths)
        for option in self._options:
            ini_section = option.kwargs.get('ini_section')
            if ini_section:
                try:
                    value = config.get(ini_section, option.name)
                    results[option.name] = option.type(value)
                except ConfigParser.NoSectionError:
                    pass
        return results

    def parse_keyring(self, namespace=None):
        """."""
        results = {}
        if not keyring:
            return results
        if not namespace:
            namespace = self.prog
        for option in self._options:
            secret = keyring.get_password(namespace, option.name)
            if secret:
                results[option.name] = option.type(secret)
        return results

    def parse(self, argv=None):
        """."""
        defaults = self.get_defaults()
        args = self.parse_cli(argv=argv)
        env = self.parse_env()
        secrets = self.parse_keyring()
        ini = self.parse_ini()

        results = defaults
        results.update(ini)
        results.update(secrets)
        results.update(env)
        results.update(args)

        self._values = results
        return self

    @staticmethod
    def parse_passthru_args(argv):
        """Handles arguments to be passed thru to a subprocess using '--'.

        :returns: tuple of two lists; args and pass-thru-args
        """
        if '--' in argv:
            dashdash = argv.index("--")
            if dashdash == 0:
                return argv[1:], []
            elif dashdash > 0:
                return argv[0:dashdash], argv[dashdash + 1:]
        return argv, []

    def __repr__(self):
        return "<Config %s>" % ', '.join([
            '%s=%s' % (k, v) for k, v in self._values.iteritems()])


def comma_separated_strings(value):
    """Handles comma-separated arguments passed in command-line."""
    return map(str, value.split(","))


def comma_separated_pairs(value):
    """Handles comma-separated key/values passed in command-line."""
    pairs = value.split(",")
    results = {}
    for pair in pairs:
        key, pair_value = pair.split('=')
        results[key] = pair_value
    return results

Here’s a module I hacked together that reads command-line arguments, environment settings, ini files, and keyring values as well. It’s also available in a gist.

"""
Configuration Parser

Configurable parser that will parse config files, environment variables,
keyring, and command-line arguments.



Example test.ini file:

    [defaults]
    gini=10

    [app]
    xini = 50

Example test.arg file:

    --xfarg=30

Example test.py file:

    import os
    import sys

    import config


    def main(argv):
        '''Test.'''
        options = [
            config.Option("xpos",
                          help="positional argument",
                          nargs='?',
                          default="all",
                          env="APP_XPOS"),
            config.Option("--xarg",
                          help="optional argument",
                          default=1,
                          type=int,
                          env="APP_XARG"),
            config.Option("--xenv",
                          help="environment argument",
                          default=1,
                          type=int,
                          env="APP_XENV"),
            config.Option("--xfarg",
                          help="@file argument",
                          default=1,
                          type=int,
                          env="APP_XFARG"),
            config.Option("--xini",
                          help="ini argument",
                          default=1,
                          type=int,
                          ini_section="app",
                          env="APP_XINI"),
            config.Option("--gini",
                          help="global ini argument",
                          default=1,
                          type=int,
                          env="APP_GINI"),
            config.Option("--karg",
                          help="secret keyring arg",
                          default=-1,
                          type=int),
        ]
        ini_file_paths = [
            '/etc/default/app.ini',
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'test.ini')
        ]

        # default usage
        conf = config.Config(prog='app', options=options,
                             ini_paths=ini_file_paths)
        conf.parse()
        print conf

        # advanced usage
        cli_args = conf.parse_cli(argv=argv)
        env = conf.parse_env()
        secrets = conf.parse_keyring(namespace="app")
        ini = conf.parse_ini(ini_file_paths)
        sources = {}
        if ini:
            for key, value in ini.iteritems():
                conf[key] = value
                sources[key] = "ini-file"
        if secrets:
            for key, value in secrets.iteritems():
                conf[key] = value
                sources[key] = "keyring"
        if env:
            for key, value in env.iteritems():
                conf[key] = value
                sources[key] = "environment"
        if cli_args:
            for key, value in cli_args.iteritems():
                conf[key] = value
                sources[key] = "command-line"
        print '\n'.join(['%s:\t%s' % (k, v) for k, v in sources.items()])


    if __name__ == "__main__":
        if config.keyring:
            config.keyring.set_password("app", "karg", "13")
        main(sys.argv)

Example results:

    $APP_XENV=10 python test.py api --xarg=2 @test.arg
    <Config xpos=api, gini=1, xenv=10, xini=50, karg=13, xarg=2, xfarg=30>
    xpos:   command-line
    xenv:   environment
    xini:   ini-file
    karg:   keyring
    xarg:   command-line
    xfarg:  command-line


"""
import argparse
import ConfigParser
import copy
import os
import sys

try:
    import keyring
except ImportError:
    keyring = None


class Option(object):
    """Holds a configuration option and the names and locations for it.

    Instantiate options using the same arguments as you would for an
    add_arguments call in argparse. However, you have two additional kwargs
    available:

        env: the name of the environment variable to use for this option
        ini_section: the ini file section to look this value up from
    """

    def __init__(self, *args, **kwargs):
        self.args = args or []
        self.kwargs = kwargs or {}

    def add_argument(self, parser, **override_kwargs):
        """Add an option to a an argparse parser."""
        kwargs = {}
        if self.kwargs:
            kwargs = copy.copy(self.kwargs)
            try:
                del kwargs['env']
            except KeyError:
                pass
            try:
                del kwargs['ini_section']
            except KeyError:
                pass
        kwargs.update(override_kwargs)
        parser.add_argument(*self.args, **kwargs)

    @property
    def type(self):
        """The type of the option.

        Should be a callable to parse options.
        """
        return self.kwargs.get("type", str)

    @property
    def name(self):
        """The name of the option as determined from the args."""
        for arg in self.args:
            if arg.startswith("--"):
                return arg[2:].replace("-", "_")
            elif arg.startswith("-"):
                continue
            else:
                return arg.replace("-", "_")

    @property
    def default(self):
        """The default for the option."""
        return self.kwargs.get("default")


class Config(object):
    """Parses configuration sources."""

    def __init__(self, options=None, ini_paths=None, **parser_kwargs):
        """Initialize with list of options.

        :param ini_paths: optional paths to ini files to look up values from
        :param parser_kwargs: kwargs used to init argparse parsers.
        """
        self._parser_kwargs = parser_kwargs or {}
        self._ini_paths = ini_paths or []
        self._options = copy.copy(options) or []
        self._values = {option.name: option.default
                        for option in self._options}
        self._parser = argparse.ArgumentParser(**parser_kwargs)
        self.pass_thru_args = []

    @property
    def prog(self):
        """Program name."""
        return self._parser.prog

    def __getitem__(self, key):
        return self._values[key]

    def __setitem__(self, key, value):
        self._values[key] = value

    def __delitem__(self, key):
        del self._values[key]

    def __contains__(self, key):
        return key in self._values

    def __iter__(self):
        return iter(self._values)

    def __len__(self):
        return len(self._values)

    def get(self, key, *args):
        """
        Return the value for key if it exists otherwise the default.
        """
        return self._values.get(key, *args)

    def __getattr__(self, attr):
        if attr in self._values:
            return self._values[attr]
        else:
            raise AttributeError("'config' object has no attribute '%s'"
                                 % attr)

    def build_parser(self, options, **override_kwargs):
        """."""
        kwargs = copy.copy(self._parser_kwargs)
        kwargs.update(override_kwargs)
        if 'fromfile_prefix_chars' not in kwargs:
            kwargs['fromfile_prefix_chars'] = '@'
        parser = argparse.ArgumentParser(**kwargs)
        if options:
            for option in options:
                option.add_argument(parser)
        return parser

    def parse_cli(self, argv=None):
        """Parse command-line arguments into values."""
        if not argv:
            argv = sys.argv
        options = []
        for option in self._options:
            temp = Option(*option.args, **option.kwargs)
            temp.kwargs['default'] = argparse.SUPPRESS
            options.append(temp)
        parser = self.build_parser(options=options)
        parsed, extras = parser.parse_known_args(argv[1:])
        if extras:
            valid, pass_thru = self.parse_passthru_args(argv[1:])
            parsed, extras = parser.parse_known_args(valid)
            if extras:
                raise AttributeError("Unrecognized arguments: %s" %
                                     ' ,'.join(extras))
            self.pass_thru_args = pass_thru + extras
        return vars(parsed)

    def parse_env(self):
        results = {}
        for option in self._options:
            env_var = option.kwargs.get('env')
            if env_var and env_var in os.environ:
                value = os.environ[env_var]
                results[option.name] = option.type(value)
        return results

    def get_defaults(self):
        """Use argparse to determine and return dict of defaults."""
        parser = self.build_parser(options=self._options)
        parsed, _ = parser.parse_known_args([])
        return vars(parsed)

    def parse_ini(self, paths=None):
        """Parse config files and return configuration options.

        Expects array of files that are in ini format.
        :param paths: list of paths to files to parse (uses ConfigParse logic).
                      If not supplied, uses the ini_paths value supplied on
                      initialization.
        """
        results = {}
        config = ConfigParser.SafeConfigParser()
        config.read(paths or self._ini_paths)
        for option in self._options:
            ini_section = option.kwargs.get('ini_section')
            if ini_section:
                try:
                    value = config.get(ini_section, option.name)
                    results[option.name] = option.type(value)
                except ConfigParser.NoSectionError:
                    pass
        return results

    def parse_keyring(self, namespace=None):
        """."""
        results = {}
        if not keyring:
            return results
        if not namespace:
            namespace = self.prog
        for option in self._options:
            secret = keyring.get_password(namespace, option.name)
            if secret:
                results[option.name] = option.type(secret)
        return results

    def parse(self, argv=None):
        """."""
        defaults = self.get_defaults()
        args = self.parse_cli(argv=argv)
        env = self.parse_env()
        secrets = self.parse_keyring()
        ini = self.parse_ini()

        results = defaults
        results.update(ini)
        results.update(secrets)
        results.update(env)
        results.update(args)

        self._values = results
        return self

    @staticmethod
    def parse_passthru_args(argv):
        """Handles arguments to be passed thru to a subprocess using '--'.

        :returns: tuple of two lists; args and pass-thru-args
        """
        if '--' in argv:
            dashdash = argv.index("--")
            if dashdash == 0:
                return argv[1:], []
            elif dashdash > 0:
                return argv[0:dashdash], argv[dashdash + 1:]
        return argv, []

    def __repr__(self):
        return "<Config %s>" % ', '.join([
            '%s=%s' % (k, v) for k, v in self._values.iteritems()])


def comma_separated_strings(value):
    """Handles comma-separated arguments passed in command-line."""
    return map(str, value.split(","))


def comma_separated_pairs(value):
    """Handles comma-separated key/values passed in command-line."""
    pairs = value.split(",")
    results = {}
    for pair in pairs:
        key, pair_value = pair.split('=')
        results[key] = pair_value
    return results

回答 9

您可以为此使用ChainMap。看一看我在“哪个是允许在Python的命令行中重写配置选项的最佳方法?”的示例。这样的问题。

You can use ChainMap for this. Take a look at my example that I provided for in “Which is the best way to allow configuration options be overridden at the command line in Python?” SO question.


回答 10

我构建的库甜点正是为了满足您的大多数需求。

  • 它可以通过给定的文件路径或模块名称多次加载配置文件。
  • 它从具有给定前缀的环境变量加载配置。
  • 它可以将命令行选项附加到某些单击命令

    (对不起,它不是argparse,但是单击会更好,更高级。confect在将来的版本中可能会支持argparse)。

  • 最重要的是,confect加载Python配置文件而不是JSON / YMAL / TOML / INI。就像IPython配置文件或DJANGO设置文件一样,Python配置文件非常灵活并且易于维护。

有关更多信息,请检查项目存储库中的README.rst 。请注意,它仅支持Python3.6 up。

例子

附加命令行选项

import click
from proj_X.core import conf

@click.command()
@conf.click_options
def cli():
    click.echo(f'cache_expire = {conf.api.cache_expire}')

if __name__ == '__main__':
    cli()

它会自动创建全面的帮助消息,并声明所有属性和默认值。

$ python -m proj_X.cli --help
Usage: cli.py [OPTIONS]

Options:
  --api-cache_expire INTEGER  [default: 86400]
  --api-cache_prefix TEXT     [default: proj_X_cache]
  --api-url_base_path TEXT    [default: api/v2/]
  --db-db_name TEXT           [default: proj_x]
  --db-username TEXT          [default: proj_x_admin]
  --db-password TEXT          [default: your_password]
  --db-host TEXT              [default: 127.0.0.1]
  --help                      Show this message and exit.

加载环境变量

只需一行即可加载环境变量

conf.load_envvars('proj_X')

The library confect I built is precisely to meet most of your needs.

  • It can load configuration file multiple times through given file paths or module name.
  • It loads configurations from environment variables with a given prefix.
  • It can attach command line options to some click commands

    (sorry, it’s not argparse, but click is better and much more advanced. confect might support argparse in the future release).

  • Most importantly, confect loads Python configuration files not JSON/YMAL/TOML/INI. Just like IPython profile file or DJANGO settings file, Python configuration file is flexible and easier to maintain.

For more information, please check the README.rst in the project repository. Be aware of that it supports only Python3.6 up.

Examples

Attaching command line options

import click
from proj_X.core import conf

@click.command()
@conf.click_options
def cli():
    click.echo(f'cache_expire = {conf.api.cache_expire}')

if __name__ == '__main__':
    cli()

It automatically creates a comprehensive help message with all properties and default values declared.

$ python -m proj_X.cli --help
Usage: cli.py [OPTIONS]

Options:
  --api-cache_expire INTEGER  [default: 86400]
  --api-cache_prefix TEXT     [default: proj_X_cache]
  --api-url_base_path TEXT    [default: api/v2/]
  --db-db_name TEXT           [default: proj_x]
  --db-username TEXT          [default: proj_x_admin]
  --db-password TEXT          [default: your_password]
  --db-host TEXT              [default: 127.0.0.1]
  --help                      Show this message and exit.

Loading environment variables

It only needs one line to load environment variables

conf.load_envvars('proj_X')

ImportError:Windows上没有模块命名的站点

问题:ImportError:Windows上没有模块命名的站点

我正在尝试首次安装Python。我从Python网站下载了以下安装程序: Python 2.7.1 Windows Installer(Windows二进制文件-不包括源代码)。然后,我运行安装程序,选择“所有用户”,一切都很好。我将Python安装到默认位置:

C:\Python27

接下来,要测试Python是否已正确安装,我导航到我的Python目录,并在Windows cmd提示符下运行了“ python”命令。它返回以下错误:

ImportError:没有名为站点的模块

当我执行“ python -v”时,我得到以下信息:

#installing zipimport hook
import zipimport # builtin

#ImportError: No module named site #installed zipimport钩子#clear 内置 ._
#clear sys.path #clear sys.argv
#clear sys.ps1 #clear sys.ps2
#clear sys.exitfunc #clear sys.exc_type
#clear sys.exc_value #clear sys.exc_traceback
#clear sys.last_type #clear sys.last_value
#clear sys.last_traceback #clear sys.path_hooks
#clear sys.path_importer_cache #clear sys.meta_path
#clear sys.flags #clear sys.float_info
#restore sys.stdin #恢复sys.stdout
#restore sys.stderr #cleanup
#cleanup [1 ] zipimport #cleanup [1]信号
#cleanup[1 ] exceptions #cleanup [1] _warnings
#cleanup sys 内置的
#cleanup ints: 6 unfreed ints cleanup #cleanup浮动

当我执行dir时,C:\Python27\Lib\site.py*我得到以下信息:

C:\ Users \ Mimminito> dir C:\ Python27 \ Lib \ site.py *
驱动器C中的卷没有标签。
卷序列号是DAB9-A863

C:\ Python27 \ Lib目录

13/11/2010 20:08 20,389 site.py
1文件20,389字节
0目录694,910,976字节免费

有任何想法吗?

I am trying to install Python for the first time. I downloaded the following installer from the Python website: Python 2.7.1 Windows Installer (Windows binary — does not include source). I then ran the installer, selected ‘All Users’ and all was fine. I installed Python into the default location:

C:\Python27

Next, to test that Python was installed correctly, I navigated to my Python Directory, and ran the “python” command in the windows cmd prompt. It returns me the following error:

ImportError: No module named site

When I do ‘python -v’ I get the following:

#installing zipimport hook
import zipimport # builtin
#installed zipimport hook
#ImportError: No module named site #clear builtin._
#clear sys.path #clear sys.argv
#clear sys.ps1 #clear sys.ps2
#clear sys.exitfunc #clear sys.exc_type
#clear sys.exc_value #clear sys.exc_traceback
#clear sys.last_type #clear sys.last_value
#clear sys.last_traceback #clear sys.path_hooks
#clear sys.path_importer_cache #clear sys.meta_path
#clear sys.flags #clear sys.float_info
#restore sys.stdin #restore sys.stdout
#restore sys.stderr #cleanup main
#cleanup[1] zipimport #cleanup[1] signal
#cleanup[1] exceptions #cleanup[1] _warnings
#cleanup sys #cleanup builtin
#cleanup ints: 6 unfreed ints #cleanup floats

When I do dir C:\Python27\Lib\site.py* I get the following:

C:\Users\Mimminito>dir C:\Python27\Lib\site.py*
Volume in drive C has no label.
Volume Serial Number is DAB9-A863

Directory of C:\Python27\Lib

13/11/2010 20:08 20,389 site.py
1 File(s) 20,389 bytes
0 Dir(s) 694,910,976 bytes free

Any ideas?


回答 0

我已经为自己研究了近一天的问题,终于取得了突破。试试这个:

  1. 设置PYTHONPATH / PYTHONHOME变量

    右键单击开始菜单中的“ 计算机”图标,然后转到属性。在左侧标签上,转到高级系统设置。在出现的窗口中,转到“ 高级”选项卡,然后在底部单击“ 环境变量”。单击用户变量列表,然后开始键入Python,然后重复“ 系统变量”,以确保您没有为PYTHONPATH或PYTHONHOME设置错误的变量。接下来,添加新变量(我也可以在System中而不是User中使用,尽管它也可能对User也适用):PYTHONPATH,设置为C:\ Python27 \ Lib。,设置为C:\ Python27PYTHONHOME

希望这可以帮助!

I’ve been looking into this problem for myself for almost a day and finally had a breakthrough. Try this:

  1. Setting the PYTHONPATH / PYTHONHOME variables

    Right click the Computer icon in the start menu, go to properties. On the left tab, go to Advanced system settings. In the window that comes up, go to the Advanced tab, then at the bottom click Environment Variables. Click in the list of user variables and start typing Python, and repeat for System variables, just to make certain that you don’t have mis-set variables for PYTHONPATH or PYTHONHOME. Next, add new variables (I did in System rather than User, although it may work for User too): PYTHONPATH, set to C:\Python27\Lib. PYTHONHOME, set to C:\Python27.

Hope this helps!


回答 1

快速解决方案:设置PYTHONHOME和PYTHONPATH并在PATH上包含PYTHONHOME

例如,如果您安装到c:\ Python27

set PYTHONHOME=c:\Python27
set PYTHONPATH=c:\Python27\Lib
set PATH=%PYTHONHOME%;%PATH%

确保您在PYTHON *变量上没有结尾的’\’,这似乎会使它破坏。

Quick solution: set PYTHONHOME and PYTHONPATH and include PYTHONHOME on PATH

For example if you installed to c:\Python27

set PYTHONHOME=c:\Python27
set PYTHONPATH=c:\Python27\Lib
set PATH=%PYTHONHOME%;%PATH%

Make sure you don’t have a trailing ‘\’ on the PYTHON* vars, this seems to break it aswel.


回答 2

在安装Windows Python和Cygwin Python并尝试从Cygwin运行Cygwin Python之后,出现了这个问题。我export通过PYTHONHOME = / usr /和PYTHONPATH = / usr / lib / python2.7 解决了它

I was having this issue after installing both Windows Python and Cygwin Python, and trying to run Cygwin Python from Cygwin. I solved it by exporting PYTHONHOME=/usr/ and PYTHONPATH=/usr/lib/python2.7


回答 3

确保正确设置了PYTHONHOME环境变量。如果PYTHONHOME指向无效的位置或您要运行的其他Python安装,您将收到此错误。

试试这个:

C:\>set PYTHONHOME=C:\Python27
C:\>python

setx PYTHONHOME C:\Python27

将其永久设置为后续命令提示符

Make sure your PYTHONHOME environment variable is set correctly. You will receive this error if PYTHONHOME is pointing to invalid location or to another Python installation you are trying to run.

Try this:

C:\>set PYTHONHOME=C:\Python27
C:\>python

Use

setx PYTHONHOME C:\Python27

to set this permanently for subsequent command prompts


回答 4

找到site.py并将其路径添加到PYTHONPATH中。这样可以解决您的问题。

Locate site.py and add its path in PYTHONPATH. This will solve your problem.


回答 5

您是否要从Cygwin运行Windows Python?我有同样的问题。Cygwin中的Python无法导入站点。Cmd中的Python可以工作。

看起来您需要确保通过以下方式运行PYTHONHOME和PYTHONPATH cygwin -aw来使它们成为Windows路径。另外,python似乎使用了一些不正确的路径。

我想我需要通过cygwin安装python才能正常工作。

Are you trying to run Windows Python from Cygwin? I’m having the same problem. Python in Cygwin fails to import site. Python in Cmd works.

It looks like you need to make sure you run PYTHONHOME and PYTHONPATH through cygwin -aw to make them Windows paths. Also, python seems to be using some incorrect paths.

I think I’ll need to install python through cygwin to get it working.


回答 6

对于Windows 10(按照@slckin回答),可以通过以下命令在命令行中进行设置:

setx PYTHONHOME "C:\Python27"
setx PYTHONPATH "C:\Python27\Lib"
setx PATH "%PYTHONHOME%;%PATH%"

For Windows 10 (follow up on @slckin answer), this can be set through the command line with:

setx PYTHONHOME "C:\Python27"
setx PYTHONPATH "C:\Python27\Lib"
setx PATH "%PYTHONHOME%;%PATH%"

回答 7

就我而言,问题是另一个site.py文件,由于PATH设置,它比Python \ Lib中的文件更早被解决。

环境:Windows 10 Pro,Python27。

我的桌面安装了pgAdmin,它具有文件C:\ Program Files(x86)\ pgAdmin \ venv \ Lib \ site.py。由于PATH环境变量比Python更早拥有pdAdmin的目录(显然这是一个坏主意),因此首先找到pgAdmin的site.py。

解决该问题所需要做的就是在PATH中将pgAdmin的主页移到比Python晚的地方

In my case, the issue was another site.py file, that was resolved earlier than the one from Python\Lib, due to PATH setting.

Environment: Windows 10 Pro, Python27.

My desktop has pgAdmin installed, which has file C:\Program Files (x86)\pgAdmin\venv\Lib\site.py. Because PATH environment variable had pdAdmin’s home earlier than Python (apparently a bad idea in the first place), pgAdmin’s site.py was found first.

All I had to do to fix the issue was to move pgAdmin’s home later than Python, in PATH


回答 8

对我而言,发生这种情况是因为我安装了2个版本的python-python 27和python 3.3。这两个文件夹都设置了路径变量,因此存在此问题。为了解决这个问题,我将python27移到了temp文件夹,因为我对python 3.3没问题。因此,请检查诸如PATH,PYTHONHOME之类的环境变量,因为这可能是一个问题。谢谢。

For me it happened because I had 2 versions of python installed – python 27 and python 3.3. Both these folder had path variable set, and hence there was this issue. To fix, this, I moved python27 to temp folder, as I was ok with python 3.3. So do check environment variables like PATH,PYTHONHOME as it may be a issue. Thanks.


回答 9

如果有人发现非管理员用户仍无法使用它:

错误示例:

ImportError: No module named iso8601

您需要为easy_install设置“ –always-unzip”选项:

easy_install --always-unzip python-keystoneclient

它将解压缩您的egg文件,并允许导入找到em。

If somebody will find that it’s still not working under non-admin users:

Example error:

ImportError: No module named iso8601

you need to set ‘–always-unzip’ option for easy_install:

easy_install --always-unzip python-keystoneclient

It will unzip your egg files and will allow import to find em.


回答 10

ImportError: No module named site在安装python 2.7.11时遇到了同样的问题

最初我有Python2.5,PYTHONHOME路径设置为Python2.5。我将其重命名为,C:\Python27\并解决了该问题。

I went through the same issue of ImportError: No module named site while installing python 2.7.11

Initially I had Python2.5 and the PYTHONHOME path was set to Python2.5. I renamed it to C:\Python27\ and it resolved the problem.


回答 11

您可以尝试“ 开源Active Python设置”,它是Windows的出色Python安装程序。您只需要卸载并安装版本即可。

You may try the Open Source Active Python Setup which is a well done Python installer for Windows. You just have to desinstall your version and install it…


回答 12

我对slckin的答案投了赞成票。我的问题是我考虑周全,并在路径周围加了双引号。我删除了所有三个变量的双引号:PYTHONHOME,PYTHONPATH和PATH。请注意,该文件位于cmd或bat文件中,用于设置其他工具的环境。但是,双引号可能在图标设置中很有用。打字

揭示了报价在路径中的位置而不是按预期下降。我还缩短了PATH,使其长度少于256个字符。

I up voted slckin’s answer. My problem was that I was thoughtful and added double quotes around the paths. I removed the double quotes in all of the three variables: PYTHONHOME, PYTHONPATH, and PATH. Note that this was in a cmd or bat file to setup the environment for other tools. However, the double quotes may be useful in an icon setting. Typing

set

revealed that the quotes where in the path and not dropped as expected. I also shorted the PATH so that it was less than 256 characters long.


回答 13

我有一个非常依赖Python的应用程序,并随着新版本的发布保持python 2.7.x的最新性。直到2.7.11,当我遇到相同的No module named site错误时,一切都很好。我已将PYTHONHOME设置为c:\Python27且正在运行。但是仍然有一个谜,为什么在以前的版本中不需要它,现在需要这样做。而且,如果需要,安装程序为什么不设置此变量?

I have an application which relies heavily on Python and have kept up-to-date with python 2.7.x as new versions are released. Everthing has been fine until 2.7.11 when I got the same “No module named site” error. I’ve set PYTHONHOME to c:\Python27 and it’s working. But the mystery remains why this is now needed when it wasn’t with previous releases. And, if it is needed, why doesn’t the installer set this var?


回答 14

我有同样的问题。我的解决方案是修复Python安装。(这是一个新安装,因此我没想到会出现问题,但现在已解决。)

要修复(Windows 7):

  1. 转到控制面板->程序->程序和功能
  2. 单击已安装的Python版本,然后按“卸载/更改”。
  3. 请按照说明修复安装。

I had the same problem. My solution was to repair the Python installation. (It was a new installation so I did not expect a problem but now it is solved.)

To repair (Windows 7):

  1. go to Control Panel -> Programs -> Programs and Features
  2. click on the Python version installed and then press Uninstall/Change.
  3. follow the instructions to repair the installation.

回答 15

从PyYAML主页安装yaml:http ://www.pyyaml.org/wiki/PyYAML

选择适合您的OS和Python的版本。

Install yaml from the PyYAML home pagee: http://www.pyyaml.org/wiki/PyYAML

Select the appropriate version for your OS and Python.


在virtualenv中设置环境变量

问题:在virtualenv中设置环境变量

我有一个Heroku项目,该项目使用环境变量来获取其配置,但是我首先使用virtualenv在本地测试我的应用程序。

有没有办法在virtualenv内部设置在远程计算机上定义的环境变量?

I have a Heroku project that uses environment variables to get its configuration, but I use virtualenv to test my app locally first.

Is there a way to set the environment variables defined on the remote machine inside virtualenv?


回答 0

更新资料

截至2017年5月17日,autoenv的自述文件指出direnv可能是更好的选择,并暗示autoenv将不再维护。

旧答案

我写了autoenv来做到这一点:

https://github.com/kennethreitz/autoenv

Update

As of 17th May 2017 the README of autoenv states that direnv is probably the better option and implies autoenv is no longer maintained.

Old answer

I wrote autoenv to do exactly this:

https://github.com/kennethreitz/autoenv


回答 1

如果您使用virtualenvwrapper(我强烈建议您这样做),则可以使用中具有相同名称的脚本定义不同的钩子(预激活,后激活,预停用,后停用)$VIRTUAL_ENV/bin/。您需要后激活挂钩。

$ workon myvenv

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
export DJANGO_DEBUG=True
export S3_KEY=mykey
export S3_SECRET=mysecret

$ echo $DJANGO_DEBUG
True

如果要将此配置保留在项目目录中,只需创建一个从项目目录到的符号链接$VIRTUAL_ENV/bin/postactivate

$ rm $VIRTUAL_ENV/bin/postactivate
$ ln -s .env/postactivate $VIRTUAL_ENV/bin/postactivate

您甚至可以在每次使用mkvirtualenv自动创建符号链接

清除后停用

请记住,这本身不会清除。停用virtualenv时,环境变量将保留。要对称清理,您可以添加到$VIRTUAL_ENV/bin/predeactivate

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
unset DJANGO_DEBUG

$ deactivate

$ echo $DJANGO_DEBUG

请记住,如果将其用于可能已在您的环境中设置的环境变量,则取消设置将导致它们在离开virtualenv时被完全取消设置。因此,如果完全有可能,您可以将先前的值记录在临时位置,然后在停用时重新读回。

建立:

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
if [[ -n $SOME_VAR ]]
then
    export SOME_VAR_BACKUP=$SOME_VAR
fi
export SOME_VAR=apple

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
if [[ -n $SOME_VAR_BACKUP ]]
then
    export SOME_VAR=$SOME_VAR_BACKUP
    unset SOME_VAR_BACKUP
else
    unset SOME_VAR
fi

测试:

$ echo $SOME_VAR
banana

$ workon myenv

$ echo $SOME_VAR
apple

$ deactivate

$ echo $SOME_VAR
banana

In case you’re using virtualenvwrapper (I highly recommend doing so), you can define different hooks (preactivate, postactivate, predeactivate, postdeactivate) using the scripts with the same names in $VIRTUAL_ENV/bin/. You need the postactivate hook.

$ workon myvenv

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
export DJANGO_DEBUG=True
export S3_KEY=mykey
export S3_SECRET=mysecret

$ echo $DJANGO_DEBUG
True

If you want to keep this configuration in your project directory, simply create a symlink from your project directory to $VIRTUAL_ENV/bin/postactivate.

$ rm $VIRTUAL_ENV/bin/postactivate
$ ln -s .env/postactivate $VIRTUAL_ENV/bin/postactivate

You could even automate the creation of the symlinks each time you use mkvirtualenv.

Cleaning up on deactivate

Remember that this wont clean up after itself. When you deactivate the virtualenv, the environment variable will persist. To clean up symmetrically you can add to $VIRTUAL_ENV/bin/predeactivate.

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
unset DJANGO_DEBUG

$ deactivate

$ echo $DJANGO_DEBUG

Remember that if using this for environment variables that might already be set in your environment then the unset will result in them being completely unset on leaving the virtualenv. So if that is at all probable you could record the previous value somewhere temporary then read it back in on deactivate.

Setup:

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
if [[ -n $SOME_VAR ]]
then
    export SOME_VAR_BACKUP=$SOME_VAR
fi
export SOME_VAR=apple

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
if [[ -n $SOME_VAR_BACKUP ]]
then
    export SOME_VAR=$SOME_VAR_BACKUP
    unset SOME_VAR_BACKUP
else
    unset SOME_VAR
fi

Test:

$ echo $SOME_VAR
banana

$ workon myenv

$ echo $SOME_VAR
apple

$ deactivate

$ echo $SOME_VAR
banana

回答 2

您可以尝试:

export ENVVAR=value

在virtualenv_root / bin / activate中。基本上,激活脚本是您开始使用virtualenv时执行的脚本,因此您可以在其中放置所有自定义内容。

You could try:

export ENVVAR=value

in virtualenv_root/bin/activate. Basically the activate script is what is executed when you start using the virtualenv so you can put all your customization in there.


回答 3

仅使用virtualenv(不使用virtualenvwrapper),通过activate您采购的用于激活virtualenv 的脚本即可轻松设置环境变量。

跑:

nano YOUR_ENV/bin/activate

将环境变量添加到文件末尾,如下所示:

export KEY=VALUE

如果需要,您还可以设置类似的钩子来取消设置环境变量,如Danilo Bargen在上面的出色答案中所建议的那样。

Using only virtualenv (without virtualenvwrapper), setting environment variables is easy through the activate script you sourcing in order to activate the virtualenv.

Run:

nano YOUR_ENV/bin/activate

Add the environment variables to the end of the file like this:

export KEY=VALUE

You can also set a similar hook to unset the environment variable as suggested by Danilo Bargen in his great answer above if you need.


回答 4

尽管这里有很多不错的答案,但我没有看到一个发布的解决方案,该解决方案既包括在停用时取消设置环境变量,又不需要除之外的其他库virtualenv,因此这是我的解决方案,仅涉及使用/ bin / activate进行编辑。变量MY_SERVER_NAMEMY_DATABASE_URL示例:

在激活脚本中应该有一个用于停用的定义,并且您想在其末尾取消设置变量:

deactivate () {
    ...

    # Unset My Server's variables
    unset MY_SERVER_NAME
    unset MY_DATABASE_URL
}

然后在激活脚本的末尾,设置变量:

# Set My Server's variables
export MY_SERVER_NAME="<domain for My Server>"
export MY_DATABASE_URL="<url for database>"

这样,您无需安装其他任何东西即可使它正常工作,并且最终不会在您deactivate使用virtualenv 时留下变量。

While there are a lot of nice answers here, I didn’t see a solution posted that both includes unsetting environment variables on deactivate and doesn’t require additional libraries beyond virtualenv, so here’s my solution that just involves editing /bin/activate, using the variables MY_SERVER_NAME and MY_DATABASE_URL as examples:

There should be a definition for deactivate in the activate script, and you want to unset your variables at the end of it:

deactivate () {
    ...

    # Unset My Server's variables
    unset MY_SERVER_NAME
    unset MY_DATABASE_URL
}

Then at the end of the activate script, set the variables:

# Set My Server's variables
export MY_SERVER_NAME="<domain for My Server>"
export MY_DATABASE_URL="<url for database>"

This way you don’t have to install anything else to get it working, and you don’t end up with the variables being left over when you deactivate the virtualenv.


回答 5

在virtualenv内部,可以使用两种方法进行测试。第一个是通过Heroku工具栏(https://toolbelt.heroku.com/)安装的工具。该工具是领班。它将导出本地存储在.env文件中的所有环境变量,然后在Procfile中运行应用程序进程。

如果您正在寻找一种更简单的方法,第二种方法是在本地拥有一个.env文件,然后运行:

export $(cat .env)

Locally within an virtualenv there are two methods you could use to test this. The first is a tool which is installed via the Heroku toolbelt (https://toolbelt.heroku.com/). The tool is foreman. It will export all of your environment variables that are stored in a .env file locally and then run app processes within your Procfile.

The second way if you’re looking for a lighter approach is to have a .env file locally then run:

export $(cat .env)

回答 6

安装autoenv或者通过

$ pip install autoenv

(要么)

$ brew install autoenv

然后.env在您的virtualenv项目文件夹中创建文件

$ echo "source bin/activate" > .env

现在一切正常。

Install autoenv either by

$ pip install autoenv

(or)

$ brew install autoenv

And then create .env file in your virtualenv project folder

$ echo "source bin/activate" > .env

Now everything works fine.


回答 7

如果您已经在使用Heroku,请考虑通过Foreman运行服务器。它支持一个.env仅是行列表的文件,该文件KEY=VAL将在运行前导出到您的应用。

If you’re already using Heroku, consider running your server via Foreman. It supports a .env file which is simply a list of lines with KEY=VAL that will be exported to your app before it runs.


回答 8

另一种为django设计的方法,但应该在大多数设置中都可以使用,那就是使用django-dotenv。

Another way to do it that’s designed for django, but should work in most settings, is to use django-dotenv.


回答 9

要在env目录中激活virtualenv 并导出.env使用中存储的环境变量:

source env/bin/activate && set -a; source .env; set +a

To activate virtualenv in env directory and export envinroment variables stored in .env use :

source env/bin/activate && set -a; source .env; set +a

os.getenv和os.environ.get之间的区别

问题:os.getenv和os.environ.get之间的区别

两种方法之间有什么区别吗?

>>> os.getenv('TERM')
'xterm'
>>> os.environ.get('TERM')
'xterm'

>>> os.getenv('FOOBAR', "not found") == "not found"
True
>>> os.environ.get('FOOBAR', "not found") == "not found"
True

它们似乎具有完全相同的功能。

Is there any difference at all between both approaches?

>>> os.getenv('TERM')
'xterm'
>>> os.environ.get('TERM')
'xterm'

>>> os.getenv('FOOBAR', "not found") == "not found"
True
>>> os.environ.get('FOOBAR', "not found") == "not found"
True

They seem to have the exact same functionality.


回答 0

观察到的一个区别(Python27):

os.environ如果环境变量不存在,则会引发异常。 os.getenv不引发异常,但返回None

One difference observed (Python27):

os.environ raises an exception if the environmental variable does not exist. os.getenv does not raise an exception, but returns None


回答 1

请参阅此相关线程。基本上,os.environ可以在import上找到,并且至少在CPython中os.getenv是的包装os.environ.get

编辑:在CPython中,响应评论os.getenv基本上是os.environ.get; 的快捷方式。因为os.environ在导入时加载os,只有这样,才适用 os.getenv

See this related thread. Basically, os.environ is found on import, and os.getenv is a wrapper to os.environ.get, at least in CPython.

EDIT: To respond to a comment, in CPython, os.getenv is basically a shortcut to os.environ.get ; since os.environ is loaded at import of os, and only then, the same holds for os.getenv.


回答 2

在带有iPython的Python 2.7中:

>>> import os
>>> os.getenv??
Signature: os.getenv(key, default=None)
Source:
def getenv(key, default=None):
    """Get an environment variable, return None if it doesn't exist.
    The optional second argument can specify an alternate default."""
    return environ.get(key, default)
File:      ~/venv/lib/python2.7/os.py
Type:      function

因此,我们可以得出结论os.getenv只是一个简单的包装os.environ.get

In Python 2.7 with iPython:

>>> import os
>>> os.getenv??
Signature: os.getenv(key, default=None)
Source:
def getenv(key, default=None):
    """Get an environment variable, return None if it doesn't exist.
    The optional second argument can specify an alternate default."""
    return environ.get(key, default)
File:      ~/venv/lib/python2.7/os.py
Type:      function

So we can conclude os.getenv is just a simple wrapper around os.environ.get.


回答 3

os.environ.get和之间没有功能上的区别os.getenv,但在和上设置条目之间却存在巨大差异。已损坏,因此您应默认设置为仅避免使用鼓励您用于对称的方法。os.putenvos.environos.putenvos.environ.getos.getenvos.putenv

os.putenv改变实际的OS级的环境变量,但在某种程度上,它不是通过露面os.getenvos.environ或检查环境变量的任何其他方式STDLIB:

>>> import os
>>> os.environ['asdf'] = 'fdsa'
>>> os.environ['asdf']
'fdsa'
>>> os.putenv('aaaa', 'bbbb')
>>> os.getenv('aaaa')
>>> os.environ.get('aaaa')

您可能必须对C级getenv进行ctypes调用才能在调用后查看实际的环境变量os.putenv。(启动shell子进程并要求其提供环境变量也可能会起作用,如果您对转义和--norc/ --noprofile/ / /其他任何操作都非常谨慎,则需要避免启动配置,但这似乎很难解决。)

While there is no functional difference between os.environ.get and os.getenv, there is a massive difference between os.putenv and setting entries on os.environ. os.putenv is broken, so you should default to os.environ.get simply to avoid the way os.getenv encourages you to use os.putenv for symmetry.

os.putenv changes the actual OS-level environment variables, but in a way that doesn’t show up through os.getenv, os.environ, or any other stdlib way of inspecting environment variables:

>>> import os
>>> os.environ['asdf'] = 'fdsa'
>>> os.environ['asdf']
'fdsa'
>>> os.putenv('aaaa', 'bbbb')
>>> os.getenv('aaaa')
>>> os.environ.get('aaaa')

You’d probably have to make a ctypes call to the C-level getenv to see the real environment variables after calling os.putenv. (Launching a shell subprocess and asking it for its environment variables might work too, if you’re very careful about escaping and --norc/--noprofile/anything else you need to do to avoid startup configuration, but it seems a lot harder to get right.)


回答 4

除了以上答案:

$ python3 -m timeit -s 'import os' 'os.environ.get("TERM_PROGRAM")'
200000 loops, best of 5: 1.65 usec per loop

$ python3 -m timeit -s 'import os' 'os.getenv("TERM_PROGRAM")'
200000 loops, best of 5: 1.83 usec per loop

In addition to the answers above:

$ python3 -m timeit -s 'import os' 'os.environ.get("TERM_PROGRAM")'
200000 loops, best of 5: 1.65 usec per loop

$ python3 -m timeit -s 'import os' 'os.getenv("TERM_PROGRAM")'
200000 loops, best of 5: 1.83 usec per loop

如何在Python中获取PATH环境变量分隔符?

问题:如何在Python中获取PATH环境变量分隔符?

当需要将多个目录串联在一起时(例如在可执行文件搜索路径中),存在一个与OS相关的分隔符。对于Windows ';',对于Linux':'。Python中有没有一种方法可以分割哪个字符?

在对此问题的讨论中,如何使用python找出我的python路径?,建议这样os.sep做。这个答案是错误的,因为它是目录或文件名组成部分的分隔符,等于'\\''/'

When multiple directories need to be concatenated, as in an executable search path, there is an os-dependent separator character. For Windows it’s ';', for Linux it’s ':'. Is there a way in Python to get which character to split on?

In the discussions to this question How do I find out my python path using python? , it is suggested that os.sep will do it. That answer is wrong, since it is the separator for components of a directory or filename and equates to '\\' or '/'.


回答 0


回答 1

它是os.pathsep

It is os.pathsep


回答 2

使它更加明确(对于像我这样的python新手)

import os
print(os.pathsep)

Making it a little more explicit (For python newbies like me)

import os
print(os.pathsep)

回答 3

好,所以有:

  • os.pathsep这是;并且是PATH环境变量中的分隔符;
  • os.path.sep/在Unix / Linux和\Windows中,这是路径成分之间的隔板。

相似性是造成混乱的根源。

OK, so there are:

  • os.pathsep that is ; and which is a separator in the PATH environment variable;
  • os.path.sep that is / in Unix/Linux and \ in Windows, which is a separator between path components.

The similarity is a source of confusion.


回答 4

这是您的工作目录/特定文件夹的示例路径-

 import os
 my = os.path.sep+ "testImages" + os.path.sep + "imageHidden.png"
 print(my)

Linux-的输出

/home/*******/Desktop/folder/PlayWithPy/src/testImages/imageHidden.png

Windows输出

C:\\Users\\Administrator\\Desktop\\folder\\tests\\testImages\\imageHidden.png

This is a sample path for your working directory/specific folder –

 import os
 my = os.path.sep+ "testImages" + os.path.sep + "imageHidden.png"
 print(my)

Output for Linux-

/home/*******/Desktop/folder/PlayWithPy/src/testImages/imageHidden.png

Output for Windows-

C:\\Users\\Administrator\\Desktop\\folder\\tests\\testImages\\imageHidden.png


如何在Windows中添加到PYTHONPATH,以便找到我的模块/软件包?

问题:如何在Windows中添加到PYTHONPATH,以便找到我的模块/软件包?

我有一个托管所有Django应用程序的目录(C:\My_Projects)。我想将此目录添加到我的目录中,PYTHONPATH以便直接调用应用程序。

我尝试从Windows GUI()添加C:\My_Projects\;到Windows Path变量中My Computer > Properties > Advanced System Settings > Environment Variables。但是它仍然不读取coltrane模块并生成此错误:

错误:没有名为coltrane的模块

I have a directory which hosts all of my Django apps (C:\My_Projects). I want to add this directory to my PYTHONPATH so I can call the apps directly.

I tried adding C:\My_Projects\; to my Windows Path variable from the Windows GUI (My Computer > Properties > Advanced System Settings > Environment Variables). But it still doesn’t read the coltrane module and generates this error:

Error: No module named coltrane


回答 0

您知道在Windows上对我非常有效的方法。

My Computer > Properties > Advanced System Settings > Environment Variables >

只需将路径添加为C:\ Python27(或安装python的任何位置)

要么

然后在系统变量下创建一个名为的新变量PythonPath。在这个变量中C:\Python27\Lib;C:\Python27\DLLs;C:\Python27\Lib\lib-tk;C:\other-folders-on-the-path

这是对我有用的最好方法,我在提供的任何文档中都没有找到它。

编辑:对于那些谁无法获得它,请添加

C:\ Python27;

随之而来。否则它将永远无法正常工作

You know what has worked for me really well on windows.

My Computer > Properties > Advanced System Settings > Environment Variables >

Just add the path as C:\Python27 (or wherever you installed python)

OR

Then under system variables I create a new Variable called PythonPath. In this variable I have C:\Python27\Lib;C:\Python27\DLLs;C:\Python27\Lib\lib-tk;C:\other-folders-on-the-path

This is the best way that has worked for me which I hadn’t found in any of the docs offered.

EDIT: For those who are not able to get it, Please add

C:\Python27;

along with it. Else it will never work.


回答 1

Windows 7 Professional I修改了@mongoose_za的答案,以便更轻松地更改python版本:

  1. [右键单击]计算机>属性>高级系统设置>环境变量
  2. 点击“系统变量”下的[新建]
  3. 变量名称:PY_HOME,变量值:C:\ path \ to \ python \ version
  4. 点击[确定]
  5. 找到“路径”系统变量,然后单击[编辑]
  6. 将以下内容添加到现有变量中:

    %PY_HOME%;%PY_HOME%\ Lib;%PY_HOME%\ DLLs;%PY_HOME%\ Lib \ lib-tk;

  7. 单击[确定]关闭所有窗口。

最后,检查命令提示符并输入python。你应该看到

>python [whatever version you are using]

如果需要在版本之间进行切换,则只需修改PY_HOME变量以指向正确的目录。如果您需要安装多个python版本,这将更易于管理。

Windows 7 Professional I Modified @mongoose_za’s answer to make it easier to change the python version:

  1. [Right Click]Computer > Properties >Advanced System Settings > Environment Variables
  2. Click [New] under “System Variable”
  3. Variable Name: PY_HOME, Variable Value:C:\path\to\python\version
  4. Click [OK]
  5. Locate the “Path” System variable and click [Edit]
  6. Add the following to the existing variable:

    %PY_HOME%;%PY_HOME%\Lib;%PY_HOME%\DLLs;%PY_HOME%\Lib\lib-tk;

  7. Click [OK] to close all of the windows.

As a final sanity check open a command prompt and enter python. You should see

>python [whatever version you are using]

If you need to switch between versions, you only need to modify the PY_HOME variable to point to the proper directory. This is bit easier to manage if you need multiple python versions installed.


回答 2

从Windows命令行:

set PYTHONPATH=%PYTHONPATH%;C:\My_python_lib

要永久设置PYTHONPATH,请将该行添加到中autoexec.bat。或者,如果您通过“系统属性”编辑系统变量,则该变量也将被永久更改。

From Windows command line:

set PYTHONPATH=%PYTHONPATH%;C:\My_python_lib

To set the PYTHONPATH permanently, add the line to your autoexec.bat. Alternatively, if you edit the system variable through the System Properties, it will also be changed permanently.


回答 3

您只需将您的安装路径(前C:\ Python27 \)到PATH变量系统变量。然后关闭并打开命令行,并输入’python’

Just append your installation path (ex. C:\Python27\) to the PATH variable in System variables. Then close and open your command line and type ‘python’.


回答 4

这些解决方案有效,但是它们仅在您的计算机上适用于您的代码。我会在代码中添加几行,如下所示:

import sys
if "C:\\My_Python_Lib" not in sys.path:
    sys.path.append("C:\\My_Python_Lib")

那应该照顾你的问题

These solutions work, but they work for your code ONLY on your machine. I would add a couple of lines to your code that look like this:

import sys
if "C:\\My_Python_Lib" not in sys.path:
    sys.path.append("C:\\My_Python_Lib")

That should take care of your problems


回答 5

PythonPythonPath添加到Windows环境:

  1. 打开资源管理器。
  2. 右键单击左侧导航树面板中的“计算机”
  3. 选择上下文菜单底部的“属性”
  4. 选择“高级系统设置”
  5. 点击“环境变量…”高级”选项卡中的
  6. “系统变量”下

      • PY_HOME

        C:\Python27
      • PYTHONPATH

        %PY_HOME%\Lib;%PY_HOME%\DLLs;%PY_HOME%\Lib\lib-tk;C:\another-library
    1. 附加

      • path

        %PY_HOME%;%PY_HOME%\Scripts\

Adding Python and PythonPath to the Windows environment:

  1. Open Explorer.
  2. Right-click ‘Computer’ in the Navigation Tree Panel on the left.
  3. Select ‘Properties’ at the bottom of the Context Menu.
  4. Select ‘Advanced system settings’
  5. Click ‘Environment Variables…’ in the Advanced Tab
  6. Under ‘System Variables’:

    1. Add

      • PY_HOME

        C:\Python27
        
      • PYTHONPATH

        %PY_HOME%\Lib;%PY_HOME%\DLLs;%PY_HOME%\Lib\lib-tk;C:\another-library
        
    2. Append

      • path

        %PY_HOME%;%PY_HOME%\Scripts\
        

回答 6

在python中设置路径的更简单方法是:单击开始>我的电脑>属性>高级系统设置>环境变量>第二个窗口>

选择“路径”>“编辑”>,然后添加“; C:\ Python27 \; C:\ Python27 \ Scripts \”

链接:http : //docs.python-guide.org/en/latest/starting/install/win/

The easier way to set the path in python is : click start> My Computer >Properties > Advanced System Settings > Environment Variables > second windows >

select Path > Edit > and then add “;C:\Python27\;C:\Python27\Scripts\”

link :http://docs.python-guide.org/en/latest/starting/install/win/


回答 7

您需要添加到您的PYTHONPATH变量而不是Windows PATH变量。

http://docs.python.org/using/windows.html

You need to add to your PYTHONPATH variable instead of Windows PATH variable.

http://docs.python.org/using/windows.html


回答 8

您还可以在.pth文件c:\PythonX.X夹或中添加一个包含所需目录的文件\site-packages folder,这在我开发Python软件包时通常是我的首选方法。

有关更多信息,请参见此处

You can also add a .pth file containing the desired directory in either your c:\PythonX.X folder, or your \site-packages folder, which tends to be my preferred method when I’m developing a Python package.

See here for more information.


回答 9

import sys
sys.path.append("path/to/Modules")
print sys.path

这不会在重新启动后持续存在,也不会转换为其他文件。但是,如果您不想对系统进行永久性修改,那就太好了。

import sys
sys.path.append("path/to/Modules")
print sys.path

This won’t persist over reboots or get translated to other files. It is however great if you don’t want to make a permanent modification to your system.


回答 10

成功执行此操作的最简单方法是再次运行python安装程序(首次安装后),然后:

  1. 选择修改。
  2. 检查所需的可选功能,然后单击下一步。
  3. 到这里,在“高级选项”步骤中,您必须看到一个选项“将Python添加到环境变量”。只需检查该选项,然后单击“安装”即可。 安装完成后,将添加python环境变量,您可以在任何地方轻松使用python。

The easiest way to do that successfully, is to run the python installer again (after the first installation) and then:

  1. choose Modify.
  2. check the optional features which you want and click Next.
  3. here we go, in “Advanced Options” step you must see an option saying “Add Python to environment variables”. Just check that option and click Install. When the installation is completed, python environment variables are added and you can easily use python everywhere.

回答 11

在Windows上的Python 3.4中,当我将其添加到PATH环境变量而不是PYTHONPATH 时,它可以工作。就像您在D:\ Programming \ Python34中安装了Python 3.4一样,请将其添加到PATH环境变量的末尾

;D:\Programming\Python34

关闭并重新打开命令提示符,然后执行“ python”。它将打开python shell。这也解决了我的Sublime 3问题:“ python无法识别为内部或外部命令”

In Python 3.4 on windows it worked when I added it to PATH enviroment variable instead of PYTHONPATH. Like if you have installed Python 3.4 in D:\Programming\Python34 then add this at the end of your PATH environment variable

;D:\Programming\Python34

Close and reopen command prompt and execute ‘python’. It will open the python shell. This also fixed my Sublime 3 issue of ‘python is not recognized as an internal or external command’.


回答 12

可以从上面的一些说明中设置python 2.X路径。默认情况下,Python 3将安装在C:\ Users \\ AppData \ Local \ Programs \ Python \ Python35-32 \中,因此必须将此路径添加到Windows环境中的Path变量中。

The python 2.X paths can be set from few of the above instructions. Python 3 by default will be installed in C:\Users\\AppData\Local\Programs\Python\Python35-32\ So this path has to be added to Path variable in windows environment.


回答 13

要增强PYTHONPATH,请运行regedit并导航至KEY_LOCAL_MACHINE \ SOFTWARE \ Python \ PythonCore,然后选择要使用的python版本的文件夹。该文件夹内有一个标为PythonPath的文件夹,其中一个条目指定默认安装存储模块的路径。右键单击PythonPath并选择创建一个新密钥。您可能想在将要指定模块位置的项目后命名该密钥;这样,您可以轻松地划分和跟踪路径修改。

谢谢

To augment PYTHONPATH, run regedit and navigate to KEY_LOCAL_MACHINE \SOFTWARE\Python\PythonCore and then select the folder for the python version you wish to use. Inside this is a folder labelled PythonPath, with one entry that specifies the paths where the default install stores modules. Right-click on PythonPath and choose to create a new key. You may want to name the key after the project whose module locations it will specify; this way, you can easily compartmentalize and track your path modifications.

thanks


回答 14

Python使用PYTHONPATH环境变量来指定可在Windows上从中导入模块的目录列表。运行时,您可以检查sys.path变量以查看导入内容时将搜索哪些目录。

要从命令提示符设置此变量,请使用:set PYTHONPATH=list;of;paths

要从PowerShell设置此变量,请使用: $env:PYTHONPATH=’list;of;paths’在启动Python之前

建议通过“环境变量”设置全局设置此变量,因为任何版本的Python都可以使用它,而不是您打算使用的版本。在Windows FAQ文档的Python中阅读更多内容。

The PYTHONPATH environment variable is used by Python to specify a list of directories that modules can be imported from on Windows. When running, you can inspect the sys.path variable to see which directories will be searched when you import something.

To set this variable from the Command Prompt, use: set PYTHONPATH=list;of;paths.

To set this variable from PowerShell, use: $env:PYTHONPATH=’list;of;paths’ just before you launch Python.

Setting this variable globally through the Environment Variables settings is not recommended, as it may be used by any version of Python instead of the one that you intend to use. Read more in the Python on Windows FAQ docs.


回答 15

对于尝试使用Python 3.3+实现此功能的任何人,Windows安装程序现在都提供了一个将python.exe添加到系统搜索路径的选项。在文档中阅读更多内容。

For anyone trying to achieve this with Python 3.3+, the Windows installer now includes an option to add python.exe to the system search path. Read more in the docs.


回答 16

这个问题需要一个正确的答案:

只需使用site为此工作量身定制的标准包装即可!

这是怎么做的(在同一主题上回答我自己的问题的答案):


  1. 打开Python提示符并输入
>>> import site
>>> site.USER_SITE
'C:\\Users\\ojdo\\AppData\\Roaming\\Python\\Python37\\site-packages'
...
  1. 如果此文件夹尚不存在,请创建它:
...
>>> import os
>>> os.makedirs(site.USER_SITE)
...
  1. 手动或使用类似以下代码sitecustomize.py的内容在此文件夹中创建一个包含的内容的文件FIND_MY_PACKAGES。当然,您必须更改C:\My_Projects到自定义导入位置的正确路径。
...
>>> FIND_MY_PACKAGES = """
import site
site.addsitedir(r'C:\My_Projects')
"""
>>> filename = os.path.join(site.USER_SITE, 'sitecustomize.py')
>>> with open(filename, 'w') as outfile:
...     print(FIND_MY_PACKAGES, file=outfile)

下次启动Python时,C:\My_Projects它会出现在您的中sys.path,而无需触摸系统范围的设置。奖励:以上步骤也适用于Linux!

This question needs a proper answer:

Just use the standard package site, which was made for this job!

and here is how (plagiating my own answer to my own question on the very same topic):


  1. Open a Python prompt and type
>>> import site
>>> site.USER_SITE
'C:\\Users\\ojdo\\AppData\\Roaming\\Python\\Python37\\site-packages'
...
  1. Create this folder if it does not exist yet:
...
>>> import os
>>> os.makedirs(site.USER_SITE)
...
  1. Create a file sitecustomize.py in this folder containing the content of FIND_MY_PACKAGES, either manually or using something like the following code. Of course, you have to change C:\My_Projects to the correct path to your custom import location.
...
>>> FIND_MY_PACKAGES = """
import site
site.addsitedir(r'C:\My_Projects')
"""
>>> filename = os.path.join(site.USER_SITE, 'sitecustomize.py')
>>> with open(filename, 'w') as outfile:
...     print(FIND_MY_PACKAGES, file=outfile)

And the next time you start Python, C:\My_Projects is present in your sys.path, without having to touch system-wide settings. Bonus: the above steps work on Linux, too!


回答 17

安装ArcGIS Desktop时PYTHONPATH需要设置此变量ArcPY

PYTHONPATH=C:\arcgis\bin (您的ArcGIS Home Bin)

由于某种原因,当我在Windows 7 32位系统上使用安装程序时,从未设置过它。

This PYTHONPATH variable needs to be set for ArcPY when ArcGIS Desktop is installed.

PYTHONPATH=C:\arcgis\bin (your ArcGIS home bin)

For some reason it never was set when I used the installer on a Windows 7 32-bit system.


回答 18

我按照以下步骤在Windows 10中工作了。

在环境变量下,应仅将其添加到“ 系统变量 ”的PATH下,而不应将其添加到“ 用户变量 ”下。这是一个很大的混乱,如果我们错过了,那就会浪费时间。

另外,只需尝试导航到在计算机中安装Python的路径并将其添加到PATH。这只是工作而无需添加其他任何东西。

C:\ Users \ YourUserName \ AppData \ Local \ Programs \ Python \ Python37-32

最重要的是,关闭命令提示符,重新打开,然后再次尝试键入“ python”以查看版本详细信息。在环境变量中设置路径后,需要重新启动命令提示符以查看版本。

重新启动后,在命令提示符下键入python时,您应该能够看到python提示符和以下信息:

I got it worked in Windows 10 by following below steps.

Under environment variables, you should only add it under PATH of “System Variables” and not under “User Variables“. This is a great confusion and eats time if we miss it.

Also, just try to navigate to the path where you got Python installed in your machine and add it to PATH. This just works and no need to add any other thing in my case.I added just below path and it worked.

C:\Users\YourUserName\AppData\Local\Programs\Python\Python37-32

Most important, close command prompt, re-open and then re-try typing “python” to see the version details. You need to restart command prompt to see the version after setting up the path in environment variables.

After restarting, you should be able to see the python prompt and below info when typing python in command prompt:


回答 19

可能有些晚,但这是您将路径添加到Windows环境变量的方法。

  1. 转到环境变量选项卡,您可以通过按Windows键+ Pausa inter来执行此操作。

  2. 转到高级系统设置。

  3. 单击环境变量。

  4. 在下方的窗口中搜索“路径”值。

  5. 选择它

  6. 点击编辑

  7. 在该行的末尾,添加您的安装文件夹和到“脚本”文件夹的路由。

  8. 单击确定,接受器等。

完成后,输入cmd并从驱动器的任何位置编写python,它应该进入Python程序。

我的PC的示例(我有Python34

EXISTING_LINES;C:\Python34;C:\Python34\Scripts\

希望能帮助到你。

波哥大的问候

Maybe a little late, but this is how you add the path to the Windows Environment Variables.

  1. Go to the Environment Variables tab, you do this by pressing Windows key + Pausa inter.

  2. Go to Advanced System Settings.

  3. Click on Environment Variables.

  4. On the lower window search for the ‘Path’ value.

  5. Select it

  6. Click on Edit

  7. In the end of the line add your instalation folder and the route to ‘Scripts’ folder.

  8. Click ok, aceptar etc.

You’re done, enter cmd and write python from any location of your drive, it should enter the Python program.

Example with my pc (I have Python34)

EXISTING_LINES;C:\Python34;C:\Python34\Scripts\

Hope it helps.

Greetings from Bogotá


回答 20

您可以通过命令提示符轻松设置路径变量。

  1. 打开运行并编写cmd

  2. 在命令窗口中,输入以下内容:set path =%path%; C:\ python36

  3. 按回车。
  4. 检查写python并输入。您将看到python版本,如图所示。

You can set the path variable for easily by command prompt.

  1. Open run and write cmd

  2. In the command window write the following: set path=%path%;C:\python36

  3. press enter.
  4. to check write python and enter. You will see the python version as shown in the picture.


回答 21

虽然这个问题是关于“真正的” Python的,但确实是在网络搜索“ Iron Python PYTHONPATH”中出现的。对于像我一样困惑的Iron Python用户:事实证明Iron Python寻找一个名为的环境变量IRONPYTHONPATH

Linux / Mac / POSIX用户:不要忘记Windows不仅\用作路径分隔符,而且还;用作路径定界符,而不是:

While this question is about the ‘real’ Python, it did come up in a websearch for ‘Iron Python PYTHONPATH’. For Iron Python users as confused as I was: It turns out that Iron Python looks for an environment variable called IRONPYTHONPATH.

Linux/Mac/POSIX users: Don’t forget that not only does Windows use \ as path separators, but it also uses ; as path delimiters, not :.


如何在Python中设置环境变量

问题:如何在Python中设置环境变量

我需要在python脚本中设置一些环境变量,并且我希望从python调用的所有其他脚本来查看设置的环境变量。

如果我做

os.environ["DEBUSSY"] = 1`

它抱怨说1必须是字符串。

我还想知道一旦设置好如何在python(在脚本的后半部分)中读取环境变量。

I need to set some environment variables in the python script and I want all the other scripts that are called from python to see the environment variables set.

If I do

os.environ["DEBUSSY"] = 1`

it complains saying that 1 has to be string.

I also want to know how to read the environment variables in python (in the later part of the script) once I set it.


回答 0

环境变量必须是字符串,因此请使用

os.environ["DEBUSSY"] = "1"

将变量DEBUSSY设置为字符串1

以后要访问此变量,只需使用:

print(os.environ["DEBUSSY"])

子进程会自动继承父进程的环境变量-无需您执行任何特殊操作。

Environment variables must be strings, so use

os.environ["DEBUSSY"] = "1"

to set the variable DEBUSSY to the string 1.

To access this variable later, simply use:

print(os.environ["DEBUSSY"])

Child processes automatically inherit the environment variables of the parent process — no special action on your part is required.


回答 1

您可能需要考虑其他方面的代码健壮性;

当您将整数值的变量存储为环境变量时,请尝试

os.environ['DEBUSSY'] = str(myintvariable)

然后为了进行检索,请考虑为避免错误,应尝试

os.environ.get('DEBUSSY', 'Not Set')

可能用“ -1”代替“未设置”

所以,把所有这些放在一起

myintvariable = 1
os.environ['DEBUSSY'] = str(myintvariable)
strauss = int(os.environ.get('STRAUSS', '-1'))
# NB KeyError <=> strauss = os.environ['STRAUSS']
debussy = int(os.environ.get('DEBUSSY', '-1'))

print "%s %u, %s %u" % ('Strauss', strauss, 'Debussy', debussy)

You may need to consider some further aspects for code robustness;

when you’re storing an integer-valued variable as an environment variable, try

os.environ['DEBUSSY'] = str(myintvariable)

then for retrieval, consider that to avoid errors, you should try

os.environ.get('DEBUSSY', 'Not Set')

possibly substitute ‘-1’ for ‘Not Set’

so, to put that all together

myintvariable = 1
os.environ['DEBUSSY'] = str(myintvariable)
strauss = int(os.environ.get('STRAUSS', '-1'))
# NB KeyError <=> strauss = os.environ['STRAUSS']
debussy = int(os.environ.get('DEBUSSY', '-1'))

print "%s %u, %s %u" % ('Strauss', strauss, 'Debussy', debussy)

回答 2

os.environ行为类似于python字典,因此可以执行所有常见的字典操作。除了其他答案中提到的getset操作之外,我们还可以简单地检查键是否存在。键和值应存储为字符串

Python 3

对于python 3,字典使用in关键字而不是has_key

>>> import os
>>> 'HOME' in os.environ  # Check an existing env. variable
True
...

Python 2

>>> import os
>>> os.environ.has_key('HOME')  # Check an existing env. variable
True
>>> os.environ.has_key('FOO')   # Check for a non existing variable
False
>>> os.environ['FOO'] = '1'     # Set a new env. variable (String value)
>>> os.environ.has_key('FOO')
True
>>> os.environ.get('FOO')       # Retrieve the value
'1'

使用时要注意一件事os.environ

尽管子进程从父进程继承了环境,但是我最近遇到了一个问题,并弄清楚了,如果在运行python脚本时还有其他脚本在更新环境,那么os.environ再次调用将不会反映最新的值

文档摘录:

首次导入os模块时(通常是在Python启动期间作为处理site.py的一部分)捕获了此映射。此时间之后对环境所做的更改不会反映在os.environ中,除非直接修改os.environ进行更改。

os.environ.data 存储所有环境变量的是一个dict对象,其中包含所有环境值:

>>> type(os.environ.data)  # changed to _data since v3.2 (refer comment below)
<type 'dict'>

os.environ behaves like a python dictionary, so all the common dictionary operations can be performed. In addition to the get and set operations mentioned in the other answers, we can also simply check if a key exists. The keys and values should be stored as strings.

Python 3

For python 3, dictionaries use the in keyword instead of has_key

>>> import os
>>> 'HOME' in os.environ  # Check an existing env. variable
True
...

Python 2

>>> import os
>>> os.environ.has_key('HOME')  # Check an existing env. variable
True
>>> os.environ.has_key('FOO')   # Check for a non existing variable
False
>>> os.environ['FOO'] = '1'     # Set a new env. variable (String value)
>>> os.environ.has_key('FOO')
True
>>> os.environ.get('FOO')       # Retrieve the value
'1'

There is one important thing to note about using os.environ:

Although child processes inherit the environment from the parent process, I had run into an issue recently and figured out, if you have other scripts updating the environment while your python script is running, calling os.environ again will not reflect the latest values.

Excerpt from the docs:

This mapping is captured the first time the os module is imported, typically during Python startup as part of processing site.py. Changes to the environment made after this time are not reflected in os.environ, except for changes made by modifying os.environ directly.

os.environ.data which stores all the environment variables, is a dict object, which contains all the environment values:

>>> type(os.environ.data)  # changed to _data since v3.2 (refer comment below)
<type 'dict'>

回答 3

如果我做os.environ [“ DEBUSSY”] = 1,它会抱怨说1必须是字符串。

然后做

os.environ["DEBUSSY"] = "1"

我也想知道一旦我设置了如何在python(在脚本的后半部分)中读取环境变量。

只需使用os.environ["DEBUSSY"],如

some_value = os.environ["DEBUSSY"]

if i do os.environ[“DEBUSSY”] = 1, it complains saying that 1 has to be string.

Then do

os.environ["DEBUSSY"] = "1"

I also want to know how to read the environment variables in python(in the later part of the script) once i set it.

Just use os.environ["DEBUSSY"], as in

some_value = os.environ["DEBUSSY"]

回答 4

设置变量:

使用键的项目分配方法:

import os    
os.environ['DEBUSSY'] = '1'  #Environ Variable must be string not Int

获取或检查其是否存在,

由于os.environ是一个实例,您可以尝试对象方式。

方法1:

os.environ.get('DEBUSSY') # this is error free method if not will return None by default

将获得'1'作为返回值

方法2:

os.environ['DEBUSSY'] # will throw an key error if not found!

方法3:

'DEBUSSY' in os.environ  # will return Boolean True/False

方法4:

os.environ.has_key('DEBUSSY') #last 2 methods are Boolean Return so can use for conditional statements

to Set Variable:

item Assignment method using key:

import os    
os.environ['DEBUSSY'] = '1'  #Environ Variable must be string not Int

to get or to check whether its existed or not,

since os.environ is an instance you can try object way.

Method 1:

os.environ.get('DEBUSSY') # this is error free method if not will return None by default

will get '1' as return value

Method 2:

os.environ['DEBUSSY'] # will throw an key error if not found!

Method 3:

'DEBUSSY' in os.environ  # will return Boolean True/False

Method 4:

os.environ.has_key('DEBUSSY') #last 2 methods are Boolean Return so can use for conditional statements

回答 5

您应该将字符串值分配给环境变量。

os.environ["DEBUSSY"] = "1"

如果要读取或打印环境变量,请使用

print os.environ["DEBUSSY"]

此更改仅对分配了当前过程的当前过程有效,不会永久更改该值。子进程将自动继承父进程的环境。

You should assign string value to environment variable.

os.environ["DEBUSSY"] = "1"

If you want to read or print the environment variable just use

print os.environ["DEBUSSY"]

This changes will be effective only for the current process where it was assigned, it will no change the value permanently. The child processes will automatically inherit the environment of the parent process.


回答 6

os.environ["DEBUSSY"] = '1'呢 环境变量始终是字符串。

What about os.environ["DEBUSSY"] = '1'? Environment variables are always strings.


回答 7

我一直在尝试添加环境变量。我的目标是将一些用户信息存储到系统变量中,以便可以将这些变量用于将来的解决方案,以替代配置文件。但是,下面的代码中描述的方法完全没有帮助。

import os
os.environ["variable_1"] = "value_1"
os.environ["variable_2"] = "value_2"
# To Verify above code
os.environ.get("variable_1")
os.environ.get("variable_2")

这个简单的代码块运行良好,但是,这些变量存在于各自的进程中,因此您将无法在Windows系统设置的环境变量选项卡中找到它们。上面的代码几乎没有达到我的目的。这里讨论这个问题:变量保存问题

os.environ.putenv(key, value)

另一个失败的尝试。因此,最后,我通过模仿包装在os包系统类中的Windows shell命令,成功地将变量保存在窗口环境寄存器中。以下代码描述了此成功尝试。

os.system("SETX {0} {1} /M".format(key, value))

希望对您中的某些人有所帮助。

I have been trying to add environment variables. My goal was to store some user information to system variables such that I can use those variables for future solutions, as an alternative to config files. However, the method described in the code below did not help me at all.

import os
os.environ["variable_1"] = "value_1"
os.environ["variable_2"] = "value_2"
# To Verify above code
os.environ.get("variable_1")
os.environ.get("variable_2")

This simple code block works well, however, these variables exist inside the respective processes such that you will not find them in the environment variables tab of windows system settings. Pretty much above code did not serve my purpose. This problem is discussed here: variable save problem

os.environ.putenv(key, value)

Another unsuccessful attempt. So, finally, I managed to save variable successfully inside the window environment register by mimicking the windows shell commands wrapped inside the system class of os package. The following code describes this successful attempt.

os.system("SETX {0} {1} /M".format(key, value))

I hope this will be helpful for some of you.


回答 8

应该注意的是,如果您尝试将环境变量设置为bash评估,它将不会存储您期望的结果。例:

from os import environ

environ["JAVA_HOME"] = "$(/usr/libexec/java_home)"

这不会像在shell中那样对它求值,因此/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home您将获得原义表达式,而不是获取路径$(/usr/libexec/java_home)

确保在设置环境变量之前对其进行评估,如下所示:

from os import environ
from subprocess import Popen, PIPE

bash_variable = "$(/usr/libexec/java_home)"
capture = Popen(f"echo {bash_variable}", stdout=PIPE, shell=True)
std_out, std_err = capture.communicate()
return_code = capture.returncode

if return_code == 0:
    evaluated_env = std_out.decode().strip()
    environ["JAVA_HOME"] = evaluated_env
else:
    print(f"Error: Unable to find environment variable {bash_variable}")

It should be noted that if you try to set the environment variable to a bash evaluation it won’t store what you expect. Example:

from os import environ

environ["JAVA_HOME"] = "$(/usr/libexec/java_home)"

This won’t evaluate it like it does in a shell, so instead of getting /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home as a path you will get the literal expression $(/usr/libexec/java_home).

Make sure to evaluate it before setting the environment variable, like so:

from os import environ
from subprocess import Popen, PIPE

bash_variable = "$(/usr/libexec/java_home)"
capture = Popen(f"echo {bash_variable}", stdout=PIPE, shell=True)
std_out, std_err = capture.communicate()
return_code = capture.returncode

if return_code == 0:
    evaluated_env = std_out.decode().strip()
    environ["JAVA_HOME"] = evaluated_env
else:
    print(f"Error: Unable to find environment variable {bash_variable}")

回答 9

您可以使用os.environ字典来访问环境变量。

现在,我遇到的一个问题是,如果我试图用来os.system运行设置环境变量的批处理文件(在**。bat *文件中使用SET命令),那么它实际上不会为您的python环境设置它们(但对于使用该os.system函数创建的子进程)。为了实际获取在python环境中设置的变量,我使用以下脚本:

import re
import system
import os

def setEnvBat(batFilePath, verbose = False):
    SetEnvPattern = re.compile("set (\w+)(?:=)(.*)$", re.MULTILINE)
    SetEnvFile = open(batFilePath, "r")
    SetEnvText = SetEnvFile.read()
    SetEnvMatchList = re.findall(SetEnvPattern, SetEnvText)

    for SetEnvMatch in SetEnvMatchList:
        VarName=SetEnvMatch[0]
        VarValue=SetEnvMatch[1]
        if verbose:
            print "%s=%s"%(VarName,VarValue)
        os.environ[VarName]=VarValue

You can use the os.environ dictionary to access your environment variables.

Now, a problem I had is that if I tried to use os.system to run a batch file that sets your environment variables (using the SET command in a **.bat* file) it would not really set them for your python environment (but for the child process that is created with the os.system function). To actually get the variables set in the python environment, I use this script:

import re
import system
import os

def setEnvBat(batFilePath, verbose = False):
    SetEnvPattern = re.compile("set (\w+)(?:=)(.*)$", re.MULTILINE)
    SetEnvFile = open(batFilePath, "r")
    SetEnvText = SetEnvFile.read()
    SetEnvMatchList = re.findall(SetEnvPattern, SetEnvText)

    for SetEnvMatch in SetEnvMatchList:
        VarName=SetEnvMatch[0]
        VarValue=SetEnvMatch[1]
        if verbose:
            print "%s=%s"%(VarName,VarValue)
        os.environ[VarName]=VarValue

回答 10

当您使用环境变量(添加/修改/删除变量)时,一个好的做法是在函数完成时恢复以前的状态。

您可能需要类似modified_environ上下文管理器的问题来还原环境变量。

经典用法:

with modified_environ(DEBUSSY="1"):
    call_my_function()

When you play with environment variables (add/modify/remove variables), a good practice is to restore the previous state at function completion.

You may need something like the modified_environ context manager describe in this question to restore the environment variables.

Classic usage:

with modified_environ(DEBUSSY="1"):
    call_my_function()