Python list with Vcenter vm - python

I found a Python script to list all Vcenter VM attributes, but now I need to register some of attributes into a Python list (or array, dict... ).
But it doesn't works.
My getVminfos.py :
EDIT : the right file :
import argparse
import atexit
import itertools
import unicodedata
import pyVmomi
from pyVmomi import vmodl
from pyVmomi import vim
from pyVim.connect import SmartConnect, Disconnect
def GetArgs():
parser = argparse.ArgumentParser(description='Process args for retrieving all the Virtual Machines')
parser.add_argument('-s', '--host', required=True, action='store',help='Remote host to connect to')
parser.add_argument('-o', '--port', type=int, default=443, action='store',help='Port to connect on')
parser.add_argument('-u', '--user', required=True, action='store',help='User name to use when connecting to host')
parser.add_argument('-p', '--password', required=False, action='store',help='Password to use when connecting to host')
args = parser.parse_args()
return args
def print_vm_info(virtual_machine):
"""
Print information for a particular virtual machine or recurse into a
folder with depth protection
"""
Ansible_Hosts = []
Ansible_Groups = []
Ansible_Names = []
summary = virtual_machine.summary
print("Name : ", summary.config.name)
print("Template : ", summary.config.template)
#print("Path : ", summary.config.vmPathName)
print"Guest : ", str(unicodedata.normalize('NFKD', summary.config.guestFullName))
#print("Instance UUID : ", summary.config.instanceUuid)
#print("Bios UUID : ", summary.config.uuid)
print"State : ", summary.runtime.powerState
if summary.guest is not None:
ip_address = summary.guest.ipAddress
if ip_address:
Ansible_Hosts.append([ip_address])
print "Ansible_Hosts[1:15]", Ansible_Hosts[1:15]
def main():
args = GetArgs()
try:
si = SmartConnect(host=args.host,user=args.user,pwd=args.password,port=int(args.port))
if not si:
print("Could not connect to the specified host using specified "
"username and password")
return -1
atexit.register(Disconnect, si)
content = si.RetrieveContent() # get root folder
container = content.rootFolder # starting point to look into
viewType = [vim.VirtualMachine] # object types to look for
recursive = True # whether we should look into it recursively
containerView = content.viewManager.CreateContainerView(
container, viewType, recursive)
children = containerView.view
for child in children:
print_vm_info(child)
except vmodl.MethodFault as error:
print("Caught vmodl fault : " + error.msg)
return -1
return 0
# Start program
if __name__ == "__main__":
main()
Prints works like a charm, but always my lists (Ansible_Hosts, ...) are empty...

The lists initialization statements (Ansible_Hosts = [] etc.) should go to main()

Related

subprocess or multithreading or threading pool

I'm going to run the command line utility multiple times in parallel using Python.
I know that multithreading is better to use for I/O operations, multiprocessing - for CPU oriented operations.
But what should I use for parallel subprocess.run?
I also know that I can create a pool from the subprocess module, but how is it different from pools from the multiprocessing and threading modules? And why shouldn't I just put subprocess.run function into multiprocessing or threading pools?
Or maybe there are some criteria when it is better to put a utility run cmd into a pool of threads or processes?
(In my case, I'm going to run the "ffmpeg" utility)
In a situation like this, I tend to run subprocesses from a ThreadPoolExecutor, basically because it's easy.
Example (from here):
from datetime import datetime
from functools import partial
import argparse
import concurrent.futures as cf
import logging
import os
import subprocess as sp
import sys
__version__ = "2021.09.19"
def main():
"""
Entry point for dicom2jpg.
"""
args = setup()
if not args.fn:
logging.error("no files to process")
sys.exit(1)
if args.quality != 80:
logging.info(f"quality set to {args.quality}")
if args.level:
logging.info("applying level correction.")
convert_partial = partial(convert, quality=args.quality, level=args.level)
starttime = str(datetime.now())[:-7]
logging.info(f"started at {starttime}.")
with cf.ThreadPoolExecutor(max_workers=os.cpu_count()) as tp:
for infn, outfn, rv in tp.map(convert_partial, args.fn):
logging.info(f"finished conversion of {infn} to {outfn} (returned {rv})")
endtime = str(datetime.now())[:-7]
logging.info(f"completed at {endtime}.")
def setup():
"""Parse command-line arguments."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--log",
default="warning",
choices=["debug", "info", "warning", "error"],
help="logging level (defaults to 'warning')",
)
parser.add_argument("-v", "--version", action="version", version=__version__)
parser.add_argument(
"-l",
"--level",
action="store_true",
default=False,
help="Correct color levels (default: no)",
)
parser.add_argument(
"-q", "--quality", type=int, default=80, help="JPEG quailty level (default: 80)"
)
parser.add_argument(
"fn", nargs="*", metavar="filename", help="DICOM files to process"
)
args = parser.parse_args(sys.argv[1:])
logging.basicConfig(
level=getattr(logging, args.log.upper(), None),
format="%(levelname)s: %(message)s",
)
logging.debug(f"command line arguments = {sys.argv}")
logging.debug(f"parsed arguments = {args}")
# Check for requisites
try:
sp.run(["convert"], stdout=sp.DEVNULL, stderr=sp.DEVNULL)
logging.info("found “convert”")
except FileNotFoundError:
logging.error("the program “convert” cannot be found")
sys.exit(1)
return args
def convert(filename, quality, level):
"""
Convert a DICOM file to a JPEG file.
Removing the blank areas from the Philips detector.
Arguments:
filename: name of the file to convert.
quality: JPEG quality to apply
level: Boolean to indicate whether level adustment should be done.
Returns:
Tuple of (input filename, output filename, convert return value)
"""
outname = filename.strip() + ".jpg"
size = "1574x2048"
args = [
"convert",
filename,
"-units",
"PixelsPerInch",
"-density",
"300",
"-depth",
"8",
"-crop",
size + "+232+0",
"-page",
size + "+0+0",
"-auto-gamma",
"-quality",
str(quality),
]
if level:
args += ["-level", "-35%,70%,0.5"]
args.append(outname)
cp = sp.run(args, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
return (filename, outname, cp.returncode)
if __name__ == "__main__":
main()
Alternatively, you can manage a bunch of subprocesses (in the form of Popen objects) directly, as shown below.
(This was older code, now modified for Python 3)
import os
import sys
import subprocess
from multiprocessing import cpu_count
from time import sleep
def checkfor(args):
"""Make sure that a program necessary for using this script is
available.
Arguments:
args -- string or list of strings of commands. A single string may
not contain spaces.
"""
if isinstance(args, str):
if " " in args:
raise ValueError("No spaces in single command allowed.")
args = [args]
try:
with open("/dev/null", "w") as bb:
subprocess.check_call(args, stdout=bb, stderr=bb)
except Exception:
print("Required program '{}' not found! exiting.".format(args[0]))
sys.exit(1)
def startconvert(fname):
"""Use the convert(1) program from the ImageMagick suite to convert the
image and crop it."""
size = "1574x2048"
args = [
"convert",
fname,
"-units",
"PixelsPerInch",
"-density",
"300",
"-crop",
size + "+232+0",
"-page",
size + "+0+0",
fname + ".png",
]
with open("/dev/null") as bb:
p = subprocess.Popen(args, stdout=bb, stderr=bb)
print("Start processing", fname)
return (fname, p)
def manageprocs(proclist):
"""Check a list of subprocesses for processes that have ended and
remove them from the list.
"""
for it in proclist:
fn, pr = it
result = pr.poll()
if result is not None:
proclist.remove(it)
if result == 0:
print("Finished processing", fn)
else:
s = "The conversion of {} exited with error code {}."
print(s.format(fn, result))
sleep(0.5)
def main(argv):
"""Main program.
Keyword arguments:
argv -- command line arguments
"""
if len(argv) == 1:
path, binary = os.path.split(argv[0])
print("Usage: {} [file ...]".format(binary))
sys.exit(0)
del argv[0] # delete the name of the script.
checkfor("convert")
procs = []
maxprocs = cpu_count()
for ifile in argv:
while len(procs) == maxprocs:
manageprocs(procs)
procs.append(startconvert(ifile))
while len(procs) > 0:
manageprocs(procs)
# This is the main program ##
if __name__ == "__main__":
main(sys.argv)

How to use Wave file as input in VOSK speech recognition?

I have a project that needs to get a recorded file and then process by the code and extract the text from file and match the extracted file with the other text and verify it.
my problem is:
I can't use recorded file in code and it does'nt read the file
init function is the fundamental of code.
verify functtion confirm the matched speech and text.
import argparse
import json
import os
import queue
import random
import sys
from difflib import SequenceMatcher
import numpy as np
import sounddevice as sd
import vosk
q = queue.Queue()
def int_or_str(text):
"""Helper function for argument parsing."""
try:
return int(text)
except ValueError:
return text
def callback(indata, frames, time, status):
"""This is called (from a separate thread) for each audio block."""
if status:
print(status, file=sys.stderr)
q.put(bytes(indata))
def init():
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
parser.exit(0)
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-f', '--filename', type=str, metavar='FILENAME',
help='audio file to store recording to')
parser.add_argument(
'-m', '--model', type=str, metavar='MODEL_PATH',
help='Path to the model')
parser.add_argument(
'-d', '--device', type=int_or_str,
help='input device (numeric ID or substring)')
parser.add_argument(
'-r', '--samplerate', type=int, help='sampling rate')
args = parser.parse_args(remaining)
try:
if args.model is None:
args.model = "model"
if not os.path.exists(args.model):
print("Please download a model for your language from https://alphacephei.com/vosk/models")
print("and unpack as 'model' in the current folder.")
parser.exit(0)
if args.samplerate is None:
device_info = sd.query_devices(args.device, 'input')
# soundfile expects an int, sounddevice provides a float:
args.samplerate = int(device_info['default_samplerate'])
model = vosk.Model(args.model)
if args.filename:
dump_fn = open(args.filename, "wb")
else:
dump_fn = None
except KeyboardInterrupt:
print('\nDone')
parser.exit(0)
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
return model, args
def verify(random_sentence, model, args):
num, T_num, F_num, num_word = 0, 0, 0, 1
with sd.RawInputStream(samplerate=args.samplerate, blocksize=8000, device=args.device, dtype='int16',
channels=1, callback=callback):
rec = vosk.KaldiRecognizer(model, args.samplerate)
print("{}) ".format(num_word), random_sentence, end='\n')
print('=' * 30, end='\n')
run = True
while run:
data = q.get()
if rec.AcceptWaveform(data):
res = json.loads(rec.FinalResult())
res['text'] = res['text'].replace('ي', 'ی')
if SequenceMatcher(None, random_sentence, res['text']).ratio() > 0.65:
T_num, num, num_word += 1
else:
F_num, num, num_word += 1
run = False
print('=' * 30)
print('True Cases : {}\n False Cases : {}'.format(T_num, F_num))
if __name__ == "__main__":
model, args = init()
verify(random_sentences, model, args)
I have been working on a similar project. I modified the code from VOSK Git repo and wrote the following function that takes file name / path as the input and outputs the captured text. Sometimes, when there is a long pause (~seconds) in the audio file, the returned text would be an empty string. To remedy this problem, I had to write additional code that picks out the longest string that was captured. I could make do with this fix.
def get_text_from_voice(filename):
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(filename, "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
model = Model("model")
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetWords(True)
text_lst =[]
p_text_lst = []
p_str = []
len_p_str = []
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
text_lst.append(rec.Result())
print(rec.Result())
else:
p_text_lst.append(rec.PartialResult())
print(rec.PartialResult())
if len(text_lst) !=0:
jd = json.loads(text_lst[0])
txt_str = jd["text"]
elif len(p_text_lst) !=0:
for i in range(0,len(p_text_lst)):
temp_txt_dict = json.loads(p_text_lst[i])
p_str.append(temp_txt_dict['partial'])
len_p_str = [len(p_str[j]) for j in range(0,len(p_str))]
max_val = max(len_p_str)
indx = len_p_str.index(max_val)
txt_str = p_str[indx]
else:
txt_str =''
return txt_str
Make sure that the correct model is present in the same directory or put in the path to the model. Also, note that VOSK accepts audio files only in wav mono PCM format.

how to make my python code to run faster with Concurrent Futures library

This block of code here, fetches records from a mysql table with over 100k distinguished by unique ips. Then it takes set of ips in batch and processes on each of them.
The Problem that I face here is speed of execution and time. I tried running my script with ThreadPoolExecutor but it takes 10 hours to run over 100k records. So, to speed up I tried using ProcessPoolExecutor but it takes same amount of time.
Please suggest as what needs to be done to increase the execution speed of my script.
import sys
sys.path.insert(1, '/var/lib/hadoop-hdfs/Yogesh/pylib')
import argparse
import datetime
import sqlalchemy
import pymysql
import time
import concurrent.futures
from concurrent.futures import ProcessPoolExecutor
def get_remaining_details(db_engine, ip, device):
'''fetches model,network,region and size details for a particular device'''
try:
results = db_engine.execute("""select model,network,region_num,(select size from SSD_size where model=a.model)
from
SSD_fail_predictor a
where
inet_ntoa(machine_ip)='{0}' and device='{1}'""".
format(ip, device)).fetchone()
except Exception as exception:
sys.stderr.write("FAILED to get details from {0} with {} : {1}".format(
str(ip), str(device), str(exception)))
sys.exit(1)
return results
def insert_data(db_engine, adata, ip, device):
''' To insert the data passed into mysql'''
try:
results = db_engine.execute ("insert into ssd_dwpd values ('{0}','{1}',{days},{data_writes_gb},'{start_date}',{samples},{wpd_in_gb},'{model}',{size},{dwpd},'{network}',{region_num})".format(ip,device,**adata))
except Exception as exception:
sys.stderr.write("FAILED to insert data {0} into ssd_dwpd table: {1}".format(str(adata),str(exception)))
sys.exit(1)
return 1
def analyse_data(db_engine, ip, device):
''' How do we find drivers that have been replaced ?
first, we sort the values of all the devices in descending order
second, we create a pair of value using zip function which takes two adjacent values from sorted list
third, we take the first value from the list where the differnce is negative
so cutoff-1 is the highest index, after which is the replaced data'''
mod, net, reg, size = ('','',0,0)
details = get_remaining_details(db_engine,ip, device)
if (details == None):
with open("Void_SSD_Fail_Predictor.txt", 'a') as file1:
file1.write(ip + " : " + device + "\n")
return 1
else:
mod, net, reg, size = details
if size == None:
with open("Void_SSD_size.txt", 'a') as file2:
file2.write(ip + " : " + mod + "\n")
return 1
try:
attribute = []
results = db_engine.execute(
"select distinct attribute from lba_written where machineip='{0}' and device='{1}' order by time_stamp".format(str(ip), str(device)))
for x in results:
attribute.append(x[0])
where_clause = ''
if len(attribute) == 1:
where_clause = "machineip='{0}' and device='{1}' and value > 0 order by time_stamp".format(str(ip), str(device))
else:
attribute = attribute[-1]
where_clause = "machineip='{0}' and device='{1}' and attribute = '{2}' and value > 0 order by time_stamp".format(str(ip), str(device), attribute)
results = db_engine.execute(
"select (max(value)-min(value)) Value,count(1) as Sample,((max(time_stamp) - min(time_stamp)) / 86400) days,FROM_UNIXTIME(min(time_stamp)) start_day from lba_written where {wc}".format(wc=where_clause))
value,samples,days,start_date = results.fetchone()
if samples == 0:
with open("nullip.txt", 'a') as f:
f.write("IP: {0} with device: {1} has no samples with values > 0\n".format(ip, device))
return 1
if days > 1.0:
days = float(round(days,2))
else:
days = 1
if attribute == 'Host_Writes_32MiB':
data_writes_gb = round((value * 32) / 1024, 2)
else:
data_writes_gb = round((value * 512) / 1073741824, 2)
wpd_in_gb = round(data_writes_gb / days, 2)
dwpd = round(wpd_in_gb / size, 2)
except Exception as exception:
sys.stderr.write("For IP {0} Something is not correct here: {1}".format(
str(ip), str(exception)))
sys.exit(1)
return {
'data_writes_gb': data_writes_gb,
'days': days,
'network': net,
'start_date': start_date,
'samples': samples,
'wpd_in_gb': wpd_in_gb,
'model': mod,
'region_num': reg,
'size': size,
'dwpd': dwpd,
}
def get_devices_for_ip(db_engine, ip):
"""Fetches unique device for the passed ip"""
devices = []
try:
results = db_engine.execute(
"select distinct device from lba_written where machineip='{}'".format(str(ip)))
for row in results.fetchall():
devices.append(row[0])
except Exception as exception:
## Ideally script should never come here ##
sys.stderr.write("FAILED to get devices for {0} : {1}".format(
str(ip), str(exception)))
sys.exit(1)
return devices
def process_ips(ip):
""" Process IPs
Input:
IP address of the device
Output:
If updated 1 else 0
"""
db_engine = sqlalchemy.create_engine("mysql+pymysql://root:hdfs#xxx.xx.xx.xxx/hardware_perf")
devices = get_devices_for_ip(db_engine,ip)
for device in devices:
analysed_data = analyse_data(db_engine,ip, device)
#self.printv("Analysed data: {}".format(analysed_data))
if (isinstance(analysed_data, dict) == True):
op = insert_data(db_engine,
analysed_data, ip, device)
elif (analysed_data == 1):
op = 1
else:
op = 0
db_engine.dispose()
return op
def batcher(ips,seg_size):
for x in range(0, len(ips), seg_size):
yield ips[x:x + seg_size]
def printv(args, message):
""" Print verbose message
Print message only if VEBOSE is true
Input:
message (str): string with message
"""
if args.verbose:
print(str(message))
def get_ips_from_db(args):
'''Fetches distinct machine ips from db'''
db_engine = sqlalchemy.create_engine(
"mysql+pymysql://root:hdfs#xxx.xx.xx.xxx/hardware_perf")
ips = []
if args.limit:
sql = "select distinct machineip from lba_written limit {}".format(
int(args.limit))
else:
sql = "select distinct machineip from lba_written"
try:
results = db_engine.execute(sql)
for ip in results.fetchall():
ips.append(ip[0])
if not ips:
printv(args,"Got no device from lba_written table")
sys.exit(1)
except Exception as exception:
sys.stderr.write(
"FAILED to get IPs from db : {}".format(str(exception)))
sys.exit(1)
db_engine.dispose()
printv(args, "Number of Distinct IPs got : {}".format(len(ips)))
printv(args, "IPs are : {}".format(str(ips)))
return ips
def arg_parser():
parser = argparse.ArgumentParser(description="Calculate dwpd for SSD")
parser.add_argument(
'--host',
required=True,
help='host name of mysql server'
)
parser.add_argument(
'-u',
'--user',
required=True,
help='Username for login mysql server'
)
parser.add_argument(
'-p',
'--password',
help='password for login mysql'
)
parser.add_argument(
'--db',
required=True,
help='database name in mysql'
)
parser.add_argument(
'-l',
'--limit',
type=int,
help="limit to restrict number of IP's to be processed"
)
parser.add_argument(
'-v',
'--verbose',
default=0,
help='Verbose mode'
)
parser.add_argument(
'-bs',
'--batch_size',
type=int,
default=50,
help='Number of records to process in each batch'
)
parser.add_argument(
'-t',
'--threads',
type=int,
default = 8,
help='Number of threads'
)
return parser.parse_args()
def main():
args = arg_parser()
ips = get_ips_from_db(args)
for ips_batch in batcher(ips,args.batch_size):
with ProcessPoolExecutor (max_workers=args.threads) as executor:
try:
results = executor.map(process_ips, ips_batch)
except Exception as ex:
print ("timeout: {}".format(ex))
if __name__ == '__main__':
main()
print ("Finished")

Error when scanning subnet range, Python 2.7 port scanner

I'm currently working on this Python port scanner, I'm trying to implement a feature that will allow this port scanner to scan a local subnet.
Currently when the target IP ends in .0, it scans every IP in that subnet range, (.1 - .255) except when I run the program, returns 'cannot resolve , unknown host' for every single IP within the subnet range. The code I currently have is below:
# import modules used in port scanner
import optparse
from socket import *
from threading import *
import ipaddress
# connect-scan function, deals with connecting to the host / determining if ports are open / closed, takes arguments tgtHost, tgtPort
def connScan(tgtHost, tgtPort):
try:
connSkt = socket(AF_INET, SOCK_STREAM)
connSkt.connect((tgtHost, tgtPort))
connSkt.send('\r\n')
result = connSkt.recv(100)
# prints result if port is open
print '[+] ' + str(tgtPort) + '/tcp open'
except:
# prints result if port is closed
print '[-] ' + str(tgtPort) + '/tcp closed'
finally:
connSkt.close()
# port-scan function, takes arguments tgtHost, tgtPorts
def portScan(tgtHost, tgtPorts):
try:
# tries to get target IP address
tgtIP = gethostbyname(tgtHost)
except:
# if unsuccesful, prints out following result
print '[-] cannot resolve ' + unicode(tgtHost) + ': unknown host'
return
try:
# tries to get target address
tgtName = gethostbyaddr(tgtIP)
print '\n[+] scan results for: ' + tgtName[0]
except:
print '\n[+] scan results for: ' + tgtIP
# sets default time out to 1
setdefaulttimeout(1)
# for every port in tgtPorts
for tgtPort in tgtPorts:
# creates thread, target is connScan function, arguments are tgtHost, int(tgtPort)
t = Thread(target=connScan, args=(tgtHost, int(tgtPort)))
# starts the thread
t.start()
def main():
parser = optparse.OptionParser('usage %prog -t <target-host> -p <target-port(s)>')
parser.add_option('-t', dest='tgtHost', type='string', help='specify target host, for local subnet, use 192.168.1.0 (scans range 192.168.1.1 - 192.168.1.255')
parser.add_option('-p', dest='tgtPort', type='string', help='specify target port(s), seperated by a comma, seperate ranges with a -')
(options, args) = parser.parse_args()
if (options.tgtHost == None) | (options.tgtPort == None):
print parser.usage
exit(0)
else:
tgtHost = options.tgtHost
if tgtHost.endswith('.0'):
hosts = ipaddress.ip_network(unicode(tgtHost+'/24'))
else:
hosts = [tgtHost]
# allows ranges of ports to be used, when seperated by a -
if '-' in str(options.tgtPort):
tgtPorts = options.tgtPort.split('-')
tgtPorts = range(int(tgtPorts[0]),int(tgtPorts[1]))
else:
tgtPorts = str(options.tgtPort).split(',')
for tgtHost in hosts:
portScan(tgtHost, tgtPorts)
if __name__ == '__main__':
main()
I've been trying to find the solution for this, however have come up empty. Does anyone know whats wrong with the code?

Python: launch default mail client on the system

I'm fairly new to Python and I'm trying to write a plugin for a text editor.
I want to know if there is a way to launch default system E-Mail client from python code.
With pywin32:
import win32api
win32api.ShellExecute(0,'open','mailto:',None,None ,0)
Update
Ah, I misread your question and presumed you're on Win platform.
A platform independent solution would be open mailto link in a browser, like
import webbrowser
webbrowser.open('mailto:', new=1)
Update 2
Some additional research (in fact, first two pages of google search) revealed this excellent snippet:
#!/usr/bin/env python
'''Utilities for opening files or URLs in the registered default application
and for sending e-mail using the user's preferred composer.
'''
__version__ = '1.1'
__all__ = ['open', 'mailto']
import os
import sys
import webbrowser
import subprocess
from email.Utils import encode_rfc2231
_controllers = {}
_open = None
class BaseController(object):
'''Base class for open program controllers.'''
def __init__(self, name):
self.name = name
def open(self, filename):
raise NotImplementedError
class Controller(BaseController):
'''Controller for a generic open program.'''
def __init__(self, *args):
super(Controller, self).__init__(os.path.basename(args[0]))
self.args = list(args)
def _invoke(self, cmdline):
if sys.platform[:3] == 'win':
closefds = False
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
else:
closefds = True
startupinfo = None
if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or
sys.platform == 'darwin'):
inout = file(os.devnull, 'r+')
else:
# for TTY programs, we need stdin/out
inout = None
# if possible, put the child precess in separate process group,
# so keyboard interrupts don't affect child precess as well as
# Python
setsid = getattr(os, 'setsid', None)
if not setsid:
setsid = getattr(os, 'setpgrp', None)
pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout,
stderr=inout, close_fds=closefds,
preexec_fn=setsid, startupinfo=startupinfo)
# It is assumed that this kind of tools (gnome-open, kfmclient,
# exo-open, xdg-open and open for OSX) immediately exit after lauching
# the specific application
returncode = pipe.wait()
if hasattr(self, 'fixreturncode'):
returncode = self.fixreturncode(returncode)
return not returncode
def open(self, filename):
if isinstance(filename, basestring):
cmdline = self.args + [filename]
else:
# assume it is a sequence
cmdline = self.args + filename
try:
return self._invoke(cmdline)
except OSError:
return False
# Platform support for Windows
if sys.platform[:3] == 'win':
class Start(BaseController):
'''Controller for the win32 start progam through os.startfile.'''
def open(self, filename):
try:
os.startfile(filename)
except WindowsError:
# [Error 22] No application is associated with the specified
# file for this operation: '<URL>'
return False
else:
return True
_controllers['windows-default'] = Start('start')
_open = _controllers['windows-default'].open
# Platform support for MacOS
elif sys.platform == 'darwin':
_controllers['open']= Controller('open')
_open = _controllers['open'].open
# Platform support for Unix
else:
import commands
# #WARNING: use the private API of the webbrowser module
from webbrowser import _iscommand
class KfmClient(Controller):
'''Controller for the KDE kfmclient program.'''
def __init__(self, kfmclient='kfmclient'):
super(KfmClient, self).__init__(kfmclient, 'exec')
self.kde_version = self.detect_kde_version()
def detect_kde_version(self):
kde_version = None
try:
info = commands.getoutput('kde-config --version')
for line in info.splitlines():
if line.startswith('KDE'):
kde_version = line.split(':')[-1].strip()
break
except (OSError, RuntimeError):
pass
return kde_version
def fixreturncode(self, returncode):
if returncode is not None and self.kde_version > '3.5.4':
return returncode
else:
return os.EX_OK
def detect_desktop_environment():
'''Checks for known desktop environments
Return the desktop environments name, lowercase (kde, gnome, xfce)
or "generic"
'''
desktop_environment = 'generic'
if os.environ.get('KDE_FULL_SESSION') == 'true':
desktop_environment = 'kde'
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
desktop_environment = 'gnome'
else:
try:
info = commands.getoutput('xprop -root _DT_SAVE_MODE')
if ' = "xfce4"' in info:
desktop_environment = 'xfce'
except (OSError, RuntimeError):
pass
return desktop_environment
def register_X_controllers():
if _iscommand('kfmclient'):
_controllers['kde-open'] = KfmClient()
for command in ('gnome-open', 'exo-open', 'xdg-open'):
if _iscommand(command):
_controllers[command] = Controller(command)
def get():
controllers_map = {
'gnome': 'gnome-open',
'kde': 'kde-open',
'xfce': 'exo-open',
}
desktop_environment = detect_desktop_environment()
try:
controller_name = controllers_map[desktop_environment]
return _controllers[controller_name].open
except KeyError:
if _controllers.has_key('xdg-open'):
return _controllers['xdg-open'].open
else:
return webbrowser.open
if os.environ.get("DISPLAY"):
register_X_controllers()
_open = get()
def open(filename):
'''Open a file or an URL in the registered default application.'''
return _open(filename)
def _fix_addersses(**kwargs):
for headername in ('address', 'to', 'cc', 'bcc'):
try:
headervalue = kwargs[headername]
if not headervalue:
del kwargs[headername]
continue
elif not isinstance(headervalue, basestring):
# assume it is a sequence
headervalue = ','.join(headervalue)
except KeyError:
pass
except TypeError:
raise TypeError('string or sequence expected for "%s", '
'%s found' % (headername,
type(headervalue).__name__))
else:
translation_map = {'%': '%25', '&': '%26', '?': '%3F'}
for char, replacement in translation_map.items():
headervalue = headervalue.replace(char, replacement)
kwargs[headername] = headervalue
return kwargs
def mailto_format(**kwargs):
# #TODO: implement utf8 option
kwargs = _fix_addersses(**kwargs)
parts = []
for headername in ('to', 'cc', 'bcc', 'subject', 'body', 'attach'):
if kwargs.has_key(headername):
headervalue = kwargs[headername]
if not headervalue:
continue
if headername in ('address', 'to', 'cc', 'bcc'):
parts.append('%s=%s' % (headername, headervalue))
else:
headervalue = encode_rfc2231(headervalue) # #TODO: check
parts.append('%s=%s' % (headername, headervalue))
mailto_string = 'mailto:%s' % kwargs.get('address', '')
if parts:
mailto_string = '%s?%s' % (mailto_string, '&'.join(parts))
return mailto_string
def mailto(address, to=None, cc=None, bcc=None, subject=None, body=None,
attach=None):
'''Send an e-mail using the user's preferred composer.
Open the user's preferred e-mail composer in order to send a mail to
address(es) that must follow the syntax of RFC822. Multiple addresses
may be provided (for address, cc and bcc parameters) as separate
arguments.
All parameters provided are used to prefill corresponding fields in
the user's e-mail composer. The user will have the opportunity to
change any of this information before actually sending the e-mail.
address - specify the destination recipient
cc - specify a recipient to be copied on the e-mail
bcc - specify a recipient to be blindly copied on the e-mail
subject - specify a subject for the e-mail
body - specify a body for the e-mail. Since the user will be able
to make changes before actually sending the e-mail, this
can be used to provide the user with a template for the
e-mail text may contain linebreaks
attach - specify an attachment for the e-mail. file must point to
an existing file
'''
mailto_string = mailto_format(**locals())
return open(mailto_string)
if __name__ == '__main__':
from optparse import OptionParser
version = '%%prog %s' % __version__
usage = (
'\n\n%prog FILENAME [FILENAME(s)] -- for opening files'
'\n\n%prog -m [OPTIONS] ADDRESS [ADDRESS(es)] -- for sending e-mails'
)
parser = OptionParser(usage=usage, version=version, description=__doc__)
parser.add_option('-m', '--mailto', dest='mailto_mode', default=False,
action='store_true', help='set mailto mode. '
'If not set any other option is ignored')
parser.add_option('--cc', dest='cc', help='specify a recipient to be '
'copied on the e-mail')
parser.add_option('--bcc', dest='bcc', help='specify a recipient to be '
'blindly copied on the e-mail')
parser.add_option('--subject', dest='subject',
help='specify a subject for the e-mail')
parser.add_option('--body', dest='body', help='specify a body for the '
'e-mail. Since the user will be able to make changes '
'before actually sending the e-mail, this can be used '
'to provide the user with a template for the e-mail '
'text may contain linebreaks')
parser.add_option('--attach', dest='attach', help='specify an attachment '
'for the e-mail. file must point to an existing file')
(options, args) = parser.parse_args()
if not args:
parser.print_usage()
parser.exit(1)
if options.mailto_mode:
if not mailto(args, None, options.cc, options.bcc, options.subject,
options.body, options.attach):
sys.exit('Unable to open the e-mail client')
else:
for name in ('cc', 'bcc', 'subject', 'body', 'attach'):
if getattr(options, name):
parser.error('The "cc", "bcc", "subject", "body" and "attach" '
'options are only accepten in mailto mode')
success = False
for arg in args:
if not open(arg):
print 'Unable to open "%s"' % arg
else:
success = True
sys.exit(success)
Enjoy.
The above-mentioned code only works in Python 2.
I've modified it to work for Python 3. The "attach" parameter is no longer supported for security reasons.
#!/usr/bin/env python3
'''Utilities for opening files or URLs in the registered default application
and for sending e-mail using the user's preferred composer.
https://stackoverflow.com/a/19779373/3211506
'''
__version__ = '1.1'
__all__ = ['open', 'mailto']
import os
import sys
import webbrowser
import subprocess
from email.utils import encode_rfc2231
_controllers = {}
_open = None
fileopen = open
class BaseController(object):
'''Base class for open program controllers.'''
def __init__(self, name):
self.name = name
def open(self, filename):
raise NotImplementedError
class Controller(BaseController):
'''Controller for a generic open program.'''
def __init__(self, *args):
super(Controller, self).__init__(os.path.basename(args[0]))
self.args = list(args)
def _invoke(self, cmdline):
if sys.platform[:3] == 'win':
closefds = False
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
else:
closefds = True
startupinfo = None
if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or
sys.platform == 'darwin'):
inout = fileopen(os.devnull, 'r+')
else:
# for TTY programs, we need stdin/out
inout = None
# if possible, put the child precess in separate process group,
# so keyboard interrupts don't affect child precess as well as
# Python
setsid = getattr(os, 'setsid', None)
if not setsid:
setsid = getattr(os, 'setpgrp', None)
pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout,
stderr=inout, close_fds=closefds,
preexec_fn=setsid, startupinfo=startupinfo)
# It is assumed that this kind of tools (gnome-open, kfmclient,
# exo-open, xdg-open and open for OSX) immediately exit after lauching
# the specific application
returncode = pipe.wait()
if hasattr(self, 'fixreturncode'):
returncode = self.fixreturncode(returncode)
return not returncode
def open(self, filename):
if isinstance(filename, str):
cmdline = self.args + [filename]
else:
# assume it is a sequence
cmdline = self.args + filename
try:
return self._invoke(cmdline)
except OSError:
return False
# Platform support for Windows
if sys.platform[:3] == 'win':
class Start(BaseController):
'''Controller for the win32 start progam through os.startfile.'''
def open(self, filename):
try:
os.startfile(filename)
except WindowsError:
# [Error 22] No application is associated with the specified
# file for this operation: '<URL>'
return False
else:
return True
_controllers['windows-default'] = Start('start')
_open = _controllers['windows-default'].open
# Platform support for MacOS
elif sys.platform == 'darwin':
_controllers['open']= Controller('open')
_open = _controllers['open'].open
# Platform support for Unix
else:
import subprocess, stat
# #WARNING: use the private API of the webbrowser module
# from webbrowser import _iscommand
def _isexecutable(cmd):
if os.path.isfile(cmd):
mode = os.stat(cmd)[stat.ST_MODE]
if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
return True
return False
def _iscommand(cmd):
"""Return True if cmd is executable or can be found on the executable
search path."""
if _isexecutable(cmd):
return True
path = os.environ.get("PATH")
if not path:
return False
for d in path.split(os.pathsep):
exe = os.path.join(d, cmd)
if _isexecutable(exe):
return True
return False
class KfmClient(Controller):
'''Controller for the KDE kfmclient program.'''
def __init__(self, kfmclient='kfmclient'):
super(KfmClient, self).__init__(kfmclient, 'exec')
self.kde_version = self.detect_kde_version()
def detect_kde_version(self):
kde_version = None
try:
info = subprocess.getoutput('kde-config --version')
for line in info.splitlines():
if line.startswith('KDE'):
kde_version = line.split(':')[-1].strip()
break
except (OSError, RuntimeError):
pass
return kde_version
def fixreturncode(self, returncode):
if returncode is not None and self.kde_version > '3.5.4':
return returncode
else:
return os.EX_OK
def detect_desktop_environment():
'''Checks for known desktop environments
Return the desktop environments name, lowercase (kde, gnome, xfce)
or "generic"
'''
desktop_environment = 'generic'
if os.environ.get('KDE_FULL_SESSION') == 'true':
desktop_environment = 'kde'
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
desktop_environment = 'gnome'
else:
try:
info = subprocess.getoutput('xprop -root _DT_SAVE_MODE')
if ' = "xfce4"' in info:
desktop_environment = 'xfce'
except (OSError, RuntimeError):
pass
return desktop_environment
def register_X_controllers():
if _iscommand('kfmclient'):
_controllers['kde-open'] = KfmClient()
for command in ('gnome-open', 'exo-open', 'xdg-open'):
if _iscommand(command):
_controllers[command] = Controller(command)
def get():
controllers_map = {
'gnome': 'gnome-open',
'kde': 'kde-open',
'xfce': 'exo-open',
}
desktop_environment = detect_desktop_environment()
try:
controller_name = controllers_map[desktop_environment]
return _controllers[controller_name].open
except KeyError:
if 'xdg-open' in _controllers:
return _controllers['xdg-open'].open
else:
return webbrowser.open
if os.environ.get("DISPLAY"):
register_X_controllers()
_open = get()
def open(filename):
'''Open a file or an URL in the registered default application.'''
return _open(filename)
def _fix_addersses(**kwargs):
for headername in ('address', 'to', 'cc', 'bcc'):
try:
headervalue = kwargs[headername]
if not headervalue:
del kwargs[headername]
continue
elif not isinstance(headervalue, str):
# assume it is a sequence
headervalue = ','.join(headervalue)
except KeyError:
pass
except TypeError:
raise TypeError('string or sequence expected for "%s", '
'%s found' % (headername,
type(headervalue).__name__))
else:
translation_map = {'%': '%25', '&': '%26', '?': '%3F'}
for char, replacement in list(translation_map.items()):
headervalue = headervalue.replace(char, replacement)
kwargs[headername] = headervalue
return kwargs
def mailto_format(**kwargs):
# #TODO: implement utf8 option
kwargs = _fix_addersses(**kwargs)
parts = []
for headername in ('to', 'cc', 'bcc', 'subject', 'body'):
if headername in kwargs:
headervalue = kwargs[headername]
if not headervalue:
continue
if headername in ('address', 'to', 'cc', 'bcc'):
parts.append('%s=%s' % (headername, headervalue))
else:
headervalue = encode_rfc2231(headervalue, charset="utf-8")[7:] # #TODO: check
parts.append('%s=%s' % (headername, headervalue))
mailto_string = 'mailto:%s' % kwargs.get('address', '')
if parts:
mailto_string = '%s?%s' % (mailto_string, '&'.join(parts))
return mailto_string
def mailto(address, to=None, cc=None, bcc=None, subject=None, body=None):
'''Send an e-mail using the user's preferred composer.
Open the user's preferred e-mail composer in order to send a mail to
address(es) that must follow the syntax of RFC822. Multiple addresses
may be provided (for address, cc and bcc parameters) as separate
arguments.
All parameters provided are used to prefill corresponding fields in
the user's e-mail composer. The user will have the opportunity to
change any of this information before actually sending the e-mail.
address - specify the destination recipient
cc - specify a recipient to be copied on the e-mail
bcc - specify a recipient to be blindly copied on the e-mail
subject - specify a subject for the e-mail
body - specify a body for the e-mail. Since the user will be able
to make changes before actually sending the e-mail, this
can be used to provide the user with a template for the
e-mail text may contain linebreaks
attach - specify an attachment for the e-mail. file must point to
an existing file (UNSUPPORTED)
'''
mailto_string = mailto_format(**locals())
return open(mailto_string)
if __name__ == '__main__':
from optparse import OptionParser
version = '%%prog %s' % __version__
usage = (
'\n\n%prog FILENAME [FILENAME(s)] -- for opening files'
'\n\n%prog -m [OPTIONS] ADDRESS [ADDRESS(es)] -- for sending e-mails'
)
parser = OptionParser(usage=usage, version=version, description=__doc__)
parser.add_option('-m', '--mailto', dest='mailto_mode', default=False,
action='store_true', help='set mailto mode. '
'If not set any other option is ignored')
parser.add_option('--cc', dest='cc', help='specify a recipient to be '
'copied on the e-mail')
parser.add_option('--bcc', dest='bcc', help='specify a recipient to be '
'blindly copied on the e-mail')
parser.add_option('--subject', dest='subject',
help='specify a subject for the e-mail')
parser.add_option('--body', dest='body', help='specify a body for the '
'e-mail. Since the user will be able to make changes '
'before actually sending the e-mail, this can be used '
'to provide the user with a template for the e-mail '
'text may contain linebreaks')
parser.add_option('--attach', dest='attach', help='specify an attachment '
'for the e-mail. file must point to an existing file')
(options, args) = parser.parse_args()
if not args:
parser.print_usage()
parser.exit(1)
if options.mailto_mode:
if not mailto(args, None, options.cc, options.bcc, options.subject,
options.body, options.attach):
sys.exit('Unable to open the e-mail client')
else:
for name in ('cc', 'bcc', 'subject', 'body', 'attach'):
if getattr(options, name):
parser.error('The "cc", "bcc", "subject", "body" and "attach" '
'options are only accepten in mailto mode')
success = False
for arg in args:
if not open(arg):
print('Unable to open "%s"' % arg)
else:
success = True
sys.exit(success)

Categories

Resources