Files
odoo_source/odoo/tools/osutil.py
T
Xavier Morel 0d9b480ee7 [REM] core: useless translation mark
I misremembered that `_` would go look for the translation stuff
through the stack, but it only goes up to the caller of `_` itself.

Since `clean_filename` has no `cr`, `cursor` or `self` that's never
going to do anything, and it's thus completely useless.

closes odoo/odoo#65534

X-original-commit: ab3122448079d998e88f77704df62d1b1871e918
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-02-04 13:03:44 +00:00

172 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
"""
Some functions related to the os and os.path module
"""
import logging
import os
import re
import tempfile
import zipfile
from contextlib import contextmanager
from os.path import join as opj
_logger = logging.getLogger(__name__)
WINDOWS_RESERVED = re.compile(r'''
^
# forbidden stems: reserved keywords
(:?CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])
# even with an extension this is recommended against
(:?\..*)?
$
''', flags=re.IGNORECASE | re.VERBOSE)
def clean_filename(name, replacement=''):
""" Strips or replaces possibly problematic or annoying characters our of
the input string, in order to make it a valid filename in most operating
systems (including dropping reserved Windows filenames).
If this results in an empty string, results in "Untitled" (localized).
Allows:
* any alphanumeric character (unicode)
* underscore (_) as that's innocuous
* dot (.) except in leading position to avoid creating dotfiles
* dash (-) except in leading position to avoid annoyance / confusion with
command options
* brackets ([ and ]), while they correspond to shell *character class*
they're a common way to mark / tag files especially on windows
* parenthesis ("(" and ")"), a more natural though less common version of
the former
* space (" ")
:param str name: file name to clean up
:param str replacement:
replacement string to use for sequences of problematic input, by default
an empty string to remove them entirely, each contiguous sequence of
problems is replaced by a single replacement
:rtype: str
"""
if WINDOWS_RESERVED.match(name):
return "Untitled"
return re.sub(r'[^\w_.()\[\] -]+', replacement, name).lstrip('.-') or "Untitled"
def listdir(dir, recursive=False):
"""Allow to recursively get the file listing following symlinks, returns
paths relative to the provided `dir` except completely broken if the symlink
it follows leaves `dir`...
"""
if not recursive:
_logger.getChild('listdir').warning("Deprecated: just call os.listdir...")
dir = os.path.normpath(dir)
if not recursive:
return os.listdir(dir)
res = []
for root, _, files in os.walk(dir, followlinks=True):
r = os.path.relpath(root, dir)
# FIXME: what should happen if root is outside dir?
yield from (opj(r, f) for f in files)
return res
def walksymlinks(top, topdown=True, onerror=None):
_logger.getChild('walksymlinks').warning("Deprecated: use os.walk(followlinks=True) instead")
return os.walk(top, topdown=topdown, onerror=onerror, followlinks=True)
@contextmanager
def tempdir():
_logger.getChild('tempdir').warning("Deprecated: use tempfile.TemporaryDirectory")
with tempfile.TemporaryDirectory() as d:
yield d
def zip_dir(path, stream, include_dir=True, fnct_sort=None): # TODO add ignore list
"""
: param fnct_sort : Function to be passed to "key" parameter of built-in
python sorted() to provide flexibility of sorting files
inside ZIP archive according to specific requirements.
"""
path = os.path.normpath(path)
len_prefix = len(os.path.dirname(path)) if include_dir else len(path)
if len_prefix:
len_prefix += 1
with zipfile.ZipFile(stream, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zipf:
for dirpath, dirnames, filenames in os.walk(path):
filenames = sorted(filenames, key=fnct_sort)
for fname in filenames:
bname, ext = os.path.splitext(fname)
ext = ext or bname
if ext not in ['.pyc', '.pyo', '.swp', '.DS_Store']:
path = os.path.normpath(os.path.join(dirpath, fname))
if os.path.isfile(path):
zipf.write(path, path[len_prefix:])
if os.name != 'nt':
getppid = os.getppid
is_running_as_nt_service = lambda: False
else:
import ctypes
import win32service as ws
import win32serviceutil as wsu
# based on http://mail.python.org/pipermail/python-win32/2007-June/006174.html
_TH32CS_SNAPPROCESS = 0x00000002
class _PROCESSENTRY32(ctypes.Structure):
_fields_ = [("dwSize", ctypes.c_ulong),
("cntUsage", ctypes.c_ulong),
("th32ProcessID", ctypes.c_ulong),
("th32DefaultHeapID", ctypes.c_ulong),
("th32ModuleID", ctypes.c_ulong),
("cntThreads", ctypes.c_ulong),
("th32ParentProcessID", ctypes.c_ulong),
("pcPriClassBase", ctypes.c_ulong),
("dwFlags", ctypes.c_ulong),
("szExeFile", ctypes.c_char * 260)]
def getppid():
CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
Process32First = ctypes.windll.kernel32.Process32First
Process32Next = ctypes.windll.kernel32.Process32Next
CloseHandle = ctypes.windll.kernel32.CloseHandle
hProcessSnap = CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
current_pid = os.getpid()
try:
pe32 = _PROCESSENTRY32()
pe32.dwSize = ctypes.sizeof(_PROCESSENTRY32)
if not Process32First(hProcessSnap, ctypes.byref(pe32)):
raise OSError('Failed getting first process.')
while True:
if pe32.th32ProcessID == current_pid:
return pe32.th32ParentProcessID
if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
return None
finally:
CloseHandle(hProcessSnap)
from contextlib import contextmanager
from odoo.release import nt_service_name
def is_running_as_nt_service():
@contextmanager
def close_srv(srv):
try:
yield srv
finally:
ws.CloseServiceHandle(srv)
try:
with close_srv(ws.OpenSCManager(None, None, ws.SC_MANAGER_ALL_ACCESS)) as hscm:
with close_srv(wsu.SmartOpenService(hscm, nt_service_name, ws.SERVICE_ALL_ACCESS)) as hs:
info = ws.QueryServiceStatusEx(hs)
return info['ProcessId'] == getppid()
except Exception:
return False
if __name__ == '__main__':
from pprint import pprint as pp
pp(listdir('../report', True))