diff --git a/glances/folder_list.py b/glances/folder_list.py index 135531d2..2dccab93 100644 --- a/glances/folder_list.py +++ b/glances/folder_list.py @@ -13,22 +13,9 @@ from __future__ import unicode_literals import os from glances.timer import Timer -from glances.globals import nativestr +from glances.globals import nativestr, folder_size from glances.logger import logger -# Use the built-in version of scandir/walk if possible, otherwise -# use the scandir module version -scandir_tag = True -try: - # For Python 3.5 or higher - from os import scandir -except ImportError: - # For others... - try: - from scandir import scandir - except ImportError: - scandir_tag = False - class FolderList(object): @@ -62,12 +49,9 @@ class FolderList(object): self.first_grab = True if self.config is not None and self.config.has_section('folders'): - if scandir_tag: - # Process monitoring list - logger.debug("Folder list configuration detected") - self.__set_folder_list('folders') - else: - logger.error('Scandir not found. Please use Python 3.5+ or install the scandir lib') + # Process monitoring list + logger.debug("Folder list configuration detected") + self.__set_folder_list('folders') else: self.__folder_list = [] @@ -132,23 +116,6 @@ class FolderList(object): else: return None - def __folder_size(self, path): - """Return the size of the directory given by path - - path: """ - - ret = 0 - for f in scandir(path): - if f.is_dir(follow_symlinks=False) and (f.name != '.' or f.name != '..'): - ret += self.__folder_size(os.path.join(path, f.name)) - else: - try: - ret += f.stat().st_size - except OSError: - pass - - return ret - def update(self, key='path'): """Update the command result attributed.""" # Only continue if monitor list is not empty @@ -163,16 +130,13 @@ class FolderList(object): # Set the key (see issue #2327) self.__folder_list[i]['key'] = key # Get folder size - try: - self.__folder_list[i]['size'] = self.__folder_size(self.path(i)) - except OSError as e: - logger.debug('Cannot get folder size ({}). Error: {}'.format(self.path(i), e)) - if e.errno == 13: - # Permission denied - self.__folder_list[i]['size'] = '!' - else: - self.__folder_list[i]['size'] = '?' - # Reset the timer + self.__folder_list[i]['size'], self.__folder_list[i]['errno'] = folder_size(self.path(i)) + if self.__folder_list[i]['errno'] != 0: + logger.debug('Folder size ({} ~ {}) may not be correct. Error: {}'.format( + self.path(i), + self.__folder_list[i]['size'], + self.__folder_list[i]['errno'])) + # Reset the timer self.timer_folders[i].reset() # It is no more the first time... diff --git a/glances/globals.py b/glances/globals.py index c539c9b5..87a48817 100644 --- a/glances/globals.py +++ b/glances/globals.py @@ -372,3 +372,29 @@ def string_value_to_float(s): def file_exists(filename): """Return True if the file exists and is readable.""" return os.path.isfile(filename) and os.access(filename, os.R_OK) + + +def folder_size(path, errno=0): + """Return a tuple with the size of the directory given by path and the errno. + If an error occurs (for example one file or subfolder is not accessible), + errno is set to the error number. + + path: + errno: Should always be 0 when calling the function""" + ret_size = 0 + ret_err = errno + try: + f_list = os.scandir(path) + except OSError as e: + return 0, e.errno + for f in f_list: + if f.is_dir(follow_symlinks=False) and (f.name != '.' or f.name != '..'): + ret = folder_size(os.path.join(path, f.name), ret_err) + ret_size += ret[0] + ret_err = ret[1] + else: + try: + ret_size += f.stat().st_size + except OSError as e: + ret_err = e.errno + return ret_size, ret_err diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index d598e393..2fd00f01 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -330,6 +330,7 @@ class _GlancesCurses(object): 'PASSWORD': curses.A_PROTECT, 'SELECTED': self.selected_color, 'INFO': self.ifINFO_color, + 'ERROR': self.selected_color, } def set_cursor(self, value): diff --git a/glances/plugins/folders/model.py b/glances/plugins/folders/model.py index e1d8328b..7c5a098e 100644 --- a/glances/plugins/folders/model.py +++ b/glances/plugins/folders/model.py @@ -64,8 +64,8 @@ class PluginModel(GlancesPluginModel): def get_alert(self, stat, header=""): """Manage limits of the folder list.""" - if not isinstance(stat['size'], numbers.Number): - ret = 'DEFAULT' + if stat['errno'] != 0: + ret = 'ERROR' else: ret = 'OK' @@ -108,15 +108,15 @@ class PluginModel(GlancesPluginModel): ret.append(self.curse_new_line()) if len(i['path']) > name_max_width: # Cut path if it is too long - path = '_' + i['path'][-name_max_width + 1 :] + path = '_' + i['path'][-name_max_width + 1:] else: path = i['path'] msg = '{:{width}}'.format(nativestr(path), width=name_max_width) ret.append(self.curse_add_line(msg)) - try: + if i['errno'] != 0: + msg = '?{:>8}'.format(self.auto_unit(i['size'])) + else: msg = '{:>9}'.format(self.auto_unit(i['size'])) - except (TypeError, ValueError): - msg = '{:>9}'.format(i['size']) ret.append(self.curse_add_line(msg, self.get_alert(i, header='folder_' + i['indice']))) return ret diff --git a/setup.py b/setup.py index bb923f01..917e6cdc 100755 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ from io import open from setuptools import setup, Command -if sys.version_info < (3, 4): - print('Glances requires at least Python 3.4 to run.') +if sys.version_info < (3, 8): + print('Glances requires at least Python 3.8 to run.') sys.exit(1) # Global functions @@ -60,7 +60,6 @@ def get_install_extras_require(): 'graphitesender', 'influxdb>=1.0.0', 'influxdb-client', 'pymongo', 'kafka-python', 'pika', 'paho-mqtt', 'potsdb', 'prometheus_client', 'pyzmq', 'statsd'], - 'folders': ['scandir'], 'gpu': ['py3nvml'], 'graph': ['pygal'], 'ip': ['netifaces'],