I created a python software install with setup.py . In this software I use data files (XML file) when I install these xml file using setup.py then these files save with other files in /usr/lib/python2.7/site_packages/XYZ
. But file permission set to these files (XML Files) rwx------
means only super user(root) can read these file I want to change the file permission of XML files as rwxr-----
means current user can also read that file. How do I change the data files permission.
The correct way to do it would be to override the install
command, here is how to do it.
First in the beginning of your setup.py
add the following imports:
from setuptools.command.install import install
from distutils import log # needed for outputting information messages
Then you need to create a callable command class, here is an example where I create a command class that installs a script and makes sure that it's only executable for root
(The are other ways to that in python. For example you can always exit the script, if you UID is not 0.)
I am also using another import here:
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)
Now the class is created. I notify the installer that upon seeing install
in the command line this class should be invoked:
setup(
# keep
# all the previous keywords you had ...
# add
cmdclass={'install': OverrideInstall}
)
I hope this answer helps.
I use setup.py
to build RPMs of all kinds. The solution is a little different for me. I also think that it's more robust for two reasons:
here is a working example
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='[email protected]',
url='https://www.python.org/sigs/distutils-sig/',
data_files=data_files,
cmdclass={'bdist_rpm':bdist_rpm}
)
Let me explain what's going on in more detail. RPMs are built from a SPEC file. bdist_rpm builds a SPEC file. In a SPEC file, you can select the permissions and ownership of a file by supplying the %attr option.
In the example to make test.sh executable and owned by user 'sri' I would add %attr(755, sri, sri)
to the end of the SPEC file.
So when I override the behavior of bdist_rpm._make_spec_file all I'm doing is adding a line for each file that I want to override the permissions on.
The full SPEC file from this example would be:
%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 <[email protected]>
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
setup.py
If you just want to install an existing package without editing its setup.py
, then try
sudo sh -c 'umask 0; \
python setup.py install --installed files.txt && \
xargs chmod -R a+rX < files.txt'
setup.py
If editing the setup.py
file is an option, a quick-and-dirty fix is to add this to the top of the 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', '.'])
This will work on Linux, but perhaps not on other OSes. If you need to support other OSes, or only want to change permissions on the installed files, then replace the chmod
call with something more sophisticated like in this other answer.
I had problems with setup.py install
creating folders that were only readable by root, in addition to copying files with so that htey were only readable by root. It turns out the permissions on folders created by setup.py
were determined by my umask
, and I believe the permissions on files copied by setup.py
are preserved, but I'm less sure about this.
I think it should be the responsibility of setup.py install
to use sane defaults -- for example, when I use apt
to install software it ignores my umask
as it should.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With