在setup.py文件中设置文件权限
我用setup.py创建了一个Python软件安装包。在这个软件里,我使用了一些数据文件(XML文件)。当我通过setup.py安装这些XML文件时,它们会和其他文件一起保存在/usr/lib/python2.7/site_packages/XYZ
这个地方。不过,这些文件的权限设置成了rwx------
,这意味着只有超级用户(root)可以读取这些文件。我想把这些XML文件的权限改成rwxr-----
,这样当前用户也能读取这些文件。我该怎么改变这些数据文件的权限呢?
4 个回答
不编辑 setup.py
的解决方案
如果你只是想安装一个现有的包,而不想编辑它的 setup.py
文件,可以试试下面的命令:
sudo sh -c 'umask 0; \
python setup.py install --installed files.txt && \
xargs chmod -R a+rX < files.txt'
编辑 setup.py
的解决方案
如果可以编辑 setup.py
文件,一个简单的解决办法是在 setup.py
的开头加上以下内容:
import os, subprocess
# Set a reasonable umask.
os.umask(0o022)
# Make all local files readable and all directories listable.
subprocess.call(['chmod', '-R', 'a+rX', '.'])
这个方法在 Linux 系统上有效,但在其他操作系统上可能不行。如果你需要支持其他操作系统,或者只是想改变已安装文件的权限,可以用更复杂的方法替换 chmod
的调用,比如在这个回答中提到的那种。
解释
我在使用 setup.py install
时遇到问题,创建的文件夹只能被 root 用户读取,复制的文件也是如此。后来我发现,setup.py
创建的文件夹的权限是由我的 umask
决定的,而我认为 setup.py
复制的文件权限是保留的,不过我对此不太确定。
规范性声明 :)
我觉得 setup.py install
应该负责使用合理的默认设置——比如,当我用 apt
安装软件时,它会忽略我的 umask
,这才是应该的。
我使用 setup.py
来构建各种类型的 RPM 包。对我来说,解决方案有点不同。我觉得这样做更稳妥,主要有两个原因:
- 我可以明确地覆盖文件的权限
- 我不需要知道用户的 uid 和 gid,而是可以直接使用文本。
下面是一个有效的例子:
from distutils.core import setup
import distutils.command.bdist_rpm
import distutils.command.install
version='13'
data_files = [
('/usr/share/blah', ['README', 'test.sh']),
]
permissions = [
('/usr/share/blah', 'test.sh', '(755, sri, sri)'),
]
class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm):
def _make_spec_file(self):
spec = distutils.command.bdist_rpm.bdist_rpm._make_spec_file(self)
for path, files , perm in permissions:
##
# Add a line to the SPEC file to change the permissions of a
# specific file upon install.
#
# example:
# %attr(666, root, root) path/file
#
spec.extend(['%attr{} {}/{}'.format(perm, path, files)])
return spec
setup(name='sri-testme',
version=version,
description='This is garganbe and is only used to test the permision flag behavior',
author='Chris Gembarowski',
author_email='chrisg@summationresearch.com',
url='https://www.python.org/sigs/distutils-sig/',
data_files=data_files,
cmdclass={'bdist_rpm':bdist_rpm}
)
让我更详细地解释一下发生了什么。RPM 包是从一个 SPEC 文件构建的。bdist_rpm 会生成一个 SPEC 文件。在 SPEC 文件中,你可以通过提供 %attr 选项来选择文件的权限和所有权。
在这个例子中,为了让 test.sh 可执行并且归用户 'sri' 所有,我会在 SPEC 文件的末尾添加 %attr(755, sri, sri)
。
所以,当我覆盖 bdist_rpm._make_spec_file 的行为时,我所做的就是为每个我想要覆盖权限的文件添加一行。
这个例子的完整 SPEC 文件如下:
%define name sri-testme
%define version 13
%define unmangled_version 13
%define release 1
Summary: This is garganbe and is only used to test the permision flag behavior
Name: %{name}
Version: %{version}
Release: %{release}
Source0: %{name}-%{unmangled_version}.tar.gz
License: UNKNOWN
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
Prefix: %{_prefix}
BuildArch: noarch
Vendor: Chris Gembarowski <chrisg@summationresearch.com>
Url: https://www.python.org/sigs/distutils-sig/
%description
UNKNOWN
%prep
%setup -n %{name}-%{unmangled_version}
%build
python setup.py build
%install
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
%clean
rm -rf $RPM_BUILD_ROOT
%post
##
# sri will be turned on in the run-once script instead of here
#
%preun
#!/bin/bash
%files -f INSTALLED_FILES
%defattr(-,root,root)
%attr(755, sri, sri) /usr/share/blah/test.sh
正确的做法是重写 install
命令,下面是具体的做法。
首先,在你的 setup.py
文件开头添加以下导入:
from setuptools.command.install import install
from distutils import log # needed for outputting information messages
接下来,你需要创建一个可以调用的命令类。这里有一个例子,我创建了一个命令类,用来安装一个脚本,并确保这个脚本只能被 root
用户执行(在 Python 中还有其他方法可以做到这一点,比如如果你的用户 ID 不是 0,就可以直接退出脚本)。我在这里还使用了另一个导入:
from setuptools.command.install_scripts import install_scripts
class OverrideInstall(install):
def run(self):
uid, gid = 0, 0
mode = 0700
install.run(self) # calling install.run(self) insures that everything that happened previously still happens, so the installation does not break!
# here we start with doing our overriding and private magic ..
for filepath in self.get_outputs():
if self.install_scripts in filepath:
log.info("Overriding setuptools mode of scripts ...")
log.info("Changing ownership of %s to uid:%s gid %s" %
(filepath, uid, gid))
os.chown(filepath, uid, gid)
log.info("Changing permissions of %s to %s" %
(filepath, oct(mode)))
os.chmod(filepath, mode)
现在类已经创建好了。我通知安装程序,当命令行中出现 install
时,应该调用这个类:
setup(
# keep
# all the previous keywords you had ...
# add
cmdclass={'install': OverrideInstall}
)
希望这个回答对你有帮助。