From 4201af9d8597dd4151d9d4335103ae972030a299 Mon Sep 17 00:00:00 2001 From: Laurie Clark-Michalek Date: Fri, 10 Aug 2012 09:04:09 +0100 Subject: Added cpu module as uname -p no longer works. Fixes #8 --- archey3 | 177 ++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 94 insertions(+), 83 deletions(-) diff --git a/archey3 b/archey3 index 4684880..226c08d 100644 --- a/archey3 +++ b/archey3 @@ -3,11 +3,11 @@ # archey3 [version 0.4] # # Copyright 2010 Melik Manukyan -# Copyright 2010-2011 Laurie Clark-Michalek +# Copyright 2010-2012 Laurie Clark-Michalek # Distributed under the terms of the GNU General Public License v3. # See http://www.gnu.org/licenses/gpl.txt for the full license text. # -# Simple python script to display an Archlinux logo in ASCII art +# Simple python script to display an Archlinux logo in ASCII art # Along with basic system information. # Import libraries @@ -31,7 +31,7 @@ except ImportError: self.level = level debug = info = warn = warning = notice = error = exception = \ critical = log = lambda *a, **kw: None - + def lookup_level(_): return 0 @@ -63,7 +63,7 @@ LOGOS = {'Arch Linux': '''{c1} {c2} #########. .########` {results[13]} {c2} ######' '###### {results[14]} {c2} ;#### ####; {results[15]} -{c2} ##' '## {results[16]} +{c2} ##' '## {results[16]} {c2} #' `# {results[17]} \x1b[0m''' } @@ -85,7 +85,7 @@ DE_DICT = {'gnome-session': 'GNOME', 'lxsession': 'LXDE', '': 'None', } - + WM_DICT = { 'awesome': 'Awesome', 'beryl': 'Beryl', @@ -136,12 +136,12 @@ State = collections.namedtuple("State", "color config logger") class display(object): command_line = '' stdindata = '' - + def __init__(self, state, args=()): self.state = state # Python3 unpacking is awesome self.arg1, self.arg2, self.arg3, *_ = tuple(args) + ('', '', '') - + @staticmethod def call_command(command): """ @@ -151,7 +151,7 @@ class display(object): proc = Popen(command.split(), stdout=PIPE) proc.wait() return proc.communicate()[0].decode() - + def run_command(self): if self.command_line: if '{arg3}' in self.command_line: @@ -163,36 +163,36 @@ class display(object): cmd = self.command_line.format(arg1=self.arg1) else: cmd = self.command_line - + try: self.process = Popen(cmd.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) except Exception as e: self.state.logger.error("Could not run command {0}".format(cmd)) - + def render(self): (stdoutdata, stderrdata) = self.process.communicate(self.stdindata or None) - + return self.format_output(stdoutdata.decode()) - + def color_me(self, output, number=None, low=30, low_color='green', medium=60, medium_color='yellow', high_color='red'): if number is None and output.isdigit(): number = int(output) elif number is None: return output - + if number <= low: color_= low_color elif low < number <= medium: color_ = medium_color elif medium < number: color_ = high_color - + return '{0}{1}{2}'.format(color(self.state, color_), output, color(self.state, 'clear')) - + regex_class = re.compile("").__class__ def process_exists(self, key): global PROCESSES @@ -206,7 +206,7 @@ class display(object): @module_register("fs") class fsDisplay(display): command_line = "df -TPh {arg1}" - + conversions = { 'binary': { 'K': 2 ** 10, @@ -221,14 +221,14 @@ class fsDisplay(display): 'T': 10 ** 12, }, } - + def __init__(self, **kwargs): super().__init__(**kwargs) if not self.arg1: msg = "Did not any arguments, require one, the fs to display" self.state.logger.error(msg) raise ArgumentError(self, msg) - + def format_output(self, instring): try: decimal_point = self.call_command( @@ -243,13 +243,13 @@ class fsDisplay(display): fstype = values[1] conversion_type = self.state.config.get('fs', 'unit', fallback="si").lower() conversions = self.conversions[conversion_type] - - mount = '/root' if self.arg1 == '/' else self.arg1 + + mount = '/root' if self.arg1 == '/' else self.arg1 title = mount.split('/')[-1].title() - + low = self.state.config.getint('fs', 'low_bound', fallback=40) medium = self.state.config.getint('fs', 'medium_bound', fallback=70) - + try: #convert to straight float used_ = float(used[:-1]) * conversions[used[-1].upper()] @@ -261,7 +261,7 @@ class fsDisplay(display): return else: used = self.color_me(used, persentage, low=low, medium=medium) - + if self.state.config.getboolean("fs", "persentage", fallback=True): part = '{used} / {total} ({persentage}%) ({fstype})'.format( used=used, total=total, persentage=int(persentage), @@ -274,7 +274,7 @@ class fsDisplay(display): @module_register("ram") class ramDisplay(display): command_line = "free -m" - + def format_output(self, instring): ram = ''.join(line for line in str(instring).split('\n') if\ line.startswith('Mem:')).split() @@ -289,11 +289,11 @@ class ramDisplay(display): used = self.color_me(number=persentage, output=str(used) + ' MB') part = '{used} / {total} MB'.format(used=used, total=total) return title, part - + @module_register("sensor") class sensorDisplay(display): command_line = "sensors {arg1}" - + def __init__(self, **kwargs): super().__init__(**kwargs) @@ -305,15 +305,15 @@ class sensorDisplay(display): self.state.logger.error( "Did not get any arguments, require one, the sensor to display.") raise - + if arg_from_arg: self.arg1 = arg_from_arg else: self.arg1 = arg_from_conf - + def format_output(self, instring): tempinfo = instring.split('\n')[2::4] - + out = [] for line in tempinfo: info = [re.sub("\s\s+", "", line) for line in line.split(' ') if\ @@ -341,9 +341,9 @@ class envDisplay(display): self.state.logger.error("Did not get any arguments, require one," + " the env variable to display.") raise - + super().__init__(**kwargs) - + def render(self): argvalue = getenv(self.arg1.upper()) return ('$' + self.arg1.upper(), argvalue) @@ -351,17 +351,17 @@ class envDisplay(display): @module_register("uname") class unameDisplay(display): command_line = "uname {arg1}" - + def __init__(self, **kwargs): super().__init__(**kwargs) - + try: flag = kwargs["args"][0] except IndexError: self.state.logger.error("Did not get any arguments, require one," + " the flag to pass to uname") raise - + arg_from_conf = self.state.config.get('uname', 'argument', fallback="") arg_from_arg = flag if arg_from_arg: @@ -370,17 +370,28 @@ class unameDisplay(display): self.arg1 = '-' + arg_from_conf else: self.arg1 = '' - + def format_output(self, instring): return (UNAME_FLAG_MEANINGS[self.arg1[1]], instring) +@module_register("cpu") +class cpuDisplay(display): + command_line = "cat /proc/cpuinfo" + + def format_output(self, instring): + kv = [line.split(":") for line in instring.split("\n") if line] + infodict = {} + for k, v in kv: + infodict[k.strip()] = v.strip() + return "Processor Type", infodict["model name"] + @module_register("uptime") class uptimeDisplay(display): def render(self): with open("/proc/uptime") as upfile: raw = upfile.read() fuptime = int(raw.split('.')[0]) - + day = int(fuptime / 86400) fuptime = fuptime % 86400 hour = int(fuptime / 3600) @@ -396,7 +407,7 @@ class uptimeDisplay(display): @module_register("packages") class packageDisplay(display): command_line = "pacman -Q" - + def format_output(self, instring): return "Packages", len(instring.split('\n')) @@ -415,26 +426,26 @@ class distroCheck(display): @module_register("process") class processCheck(display): command_line = "ps -u " + getuser() - + render = lambda self: self - + def run_command(self): super().run_command() out = str(self.process.communicate()[0]) - + self._processes = set() for line in out.split("\\n"): words = line.split() if len(words) <= 3: continue - + self._processes.add(words[3]) - + def __call__(self, proc): if proc in self._processes: return True return False - + @module_register("wm") class wmDisplay(display): def render(self): @@ -466,10 +477,10 @@ class mpdDisplay(display): nothing. """ command_line = "mpc stats --host {arg1} --port {arg2}" - + def __init__(self, **kwargs): super().__init__(**kwargs) - + try: self.stat = kwargs["args"][0] except IndexError: @@ -477,7 +488,7 @@ class mpdDisplay(display): " the stat to display.") self.arg1 = self.state.config.get('mpd', 'host', fallback='localhost') self.arg2 = self.state.config.getint('mpd', 'port', fallback=6600) - + def format_output(self, instring): lines = instring.split('\n') stats = {} @@ -490,7 +501,7 @@ class mpdDisplay(display): self.state.logger.error( "Could not parse mpc output, is mpc installed?") return - + return ('{statname} in MPD database'.format(statname=self.stat.title()), stats[self.stat]) @@ -506,10 +517,10 @@ class ArcheyConfigParser(configparser.SafeConfigParser): 'display_modules': """\ distro(), uname(n), uname(r), uptime(), wm(), de(), packages(), ram(),\ - uname(p), env(editor), fs(/), mpd(albums)""" + cpu(), env(editor), fs(/), mpd(albums)""" }, } - + def read(self, file_location=None): """ Loads the config options stored in at file_location. If file_location @@ -517,12 +528,12 @@ distro(), uname(n), uname(r), uptime(), wm(), de(), packages(), ram(),\ ($XDG_CONFIG_HOME/archey3.cfg). If that does not exist, it will write a default config file to $XDG_CONFIG_HOME/archey3.cfg. """ - + config_location = os.path.expandvars(os.path.expanduser( file_location or "$XDG_CONFIG_HOME/archey3.cfg")) - + loaded = super(ArcheyConfigParser, self).read(config_location) - + if file_location == None and not loaded: self.load_default_config() self.write_config(config_location) @@ -539,7 +550,7 @@ distro(), uname(n), uname(r), uptime(), wm(), de(), packages(), ram(),\ for section, values in self.defaults.items(): if not self.has_section(section): self.add_section(section) - + for option, value in values.items(): #strip any excess spaces value = re.sub("( +)", " ", value) @@ -551,7 +562,7 @@ distro(), uname(n), uname(r), uptime(), wm(), de(), packages(), ram(),\ """ with open(location, 'w') as configfile: self.write(configfile) - + #------------ Functions ----------- @@ -566,11 +577,11 @@ def screenshot(state): print('.', end='') sys.stdout.flush() sleep(1.0/3) - + print('Say Cheese!') sys.stdout.flush() - - screenshot_command = state.config.get('core', 'screenshot_command', + + screenshot_command = state.config.get('core', 'screenshot_command', fallback="import -window root .jpg") try: subprocess.check_call( @@ -618,7 +629,7 @@ def render_class(state, cls, args): logger=Logger(cls.__name__, state.logger.level), color=state.color, config=state.config)) - + except Exception as e: state.logger.error( "Could not instantiate {0}, failed with error {1}".format( @@ -636,50 +647,50 @@ def render_class(state, cls, args): class Archey(object): DISPLAY_PARSING_REGEX = "(?P\w+)\((|(?P[\w, /]+))\)" - + def __init__(self, config, options): log_level = lookup_level(options.log_level) logger = Logger("Core", log_level) - + self.display = config.get("core", "display_modules") colorscheme = options.color or config.get( "core", "color", fallback="blue") for key in COLORS.keys(): if key == colorscheme: colorcode = COLORS[key] - + self.state = State(colorcode, config, logger) - + global PROCESSES PROCESSES = render_class(self.state, processCheck, ()) - + distro_out = render_class(self.state, distroCheck, ()) - + if not distro_out: self.state.logger.critical( "Unrecognised distribution.") raise RuntimeException("Unrecognised distribution.") - + self.distro_name = ' '.join(distro_out[1].split()[:-1]) - + def run(self, screenshot_=False): """ Actually print the logo etc, and take a screenshot if required. """ print(self.render()) - + if screenshot_: screenshot(self.state) - + def render(self): results = self.prepare_results() results = self.arrange_results(results) - + return LOGOS[self.distro_name].format(c1=color(self.state, 1), c2=color(self.state, 2), results = results ) - + def prepare_results(self): """ Renders all classes found in the display array, and then returns them @@ -687,9 +698,9 @@ class Archey(object): left over spaces being filled with empty strings. """ poolsize = self.state.config.getint("core", "poolsize", fallback=5) - + pool = multiprocessing.Pool(poolsize) - + arguments = [] for cls_name, args in self.parse_display(): arguments.append({ @@ -700,10 +711,10 @@ class Archey(object): raw_out = pool.map(_mp_render_helper, arguments) outputs = list(map(self.format_item, filter(bool, raw_out))) - - + + return outputs + [""] * (18 - len(outputs)) - + def arrange_results(self, results): """ Arranges the results as specified in the config file. @@ -721,7 +732,7 @@ class Archey(object): [""] * (len(results) - len(actuall_res))) else: return results - + def parse_display(self): """ Iterates over the display attribute of the Archey class, and tries to @@ -729,26 +740,26 @@ class Archey(object): """ for func in self.display.split(","): func = func.strip() - + info = re.match(self.DISPLAY_PARSING_REGEX, func) if not info: self.state.logger.error( "Could not parse display string {0}".format(func)) continue - + groups = info.groupdict() if groups["args"]: args = [arg.strip() for arg in groups["args"].split(",")] else: args = () - + yield groups["func"], args raise StopIteration def format_item(self, item): title = item[0].rstrip(':') data = str(item[1]).rstrip() - + #if we're dealing with a fraction if len(data.split('/')) == 2: numerator = data.split('/')[0] @@ -756,7 +767,7 @@ class Archey(object): color(self.state, 'clear')) denominator = data.split('/')[1] data = '/'.join((numerator, denominator)) - + return "{color}{title}:{clear} {data}".format( color=color(self.state, 1), title=title, @@ -799,10 +810,10 @@ def main(): help="The level of errors you wish to display. Choices are\ NOTSET, DEBUG, INFO, WARNING, ERROR, and CRITICAL. CRITICAL is the default.") (options, args) = parser.parse_args() - + config = ArcheyConfigParser() config.read(options.config) - + archey = Archey(config=config, options=options) archey.run(options.screenshot) -- cgit v1.2.3