Package: servicex
ServiceX Client LibraryClient access library for ServiceXIntroductionGiven you have a selection string, this library will manage submitting it to a ServiceX instance and retrieving the data locally for you.
The selection string is often generated by another front-end library, for example:func_adl.xAOD(for ATLAS xAOD's)func_adl.uproot(for flat ntuples)tcut_to_castle(translatesTCutlike syntax into aservicexquery - should work for both)PrerequisitesBefore you can use this library you'll need:An environment based on python 3.7 or later. 3.11 is highest supported at the moment.AServiceXend-point. This is usually gotten by logging into and getting approved at the servicex endpoint. Once you do that, you'll have an API token, which this library needs to access theServiceXendpoint, and the web address where you got that token (theendpointaddress).How to access your endpointThe API access information is normally placed in a configuration file (see the section below). Create a config file,servicex.yaml, in theyamlformat, in the appropriate place for your work that contains the following (for thexaodbackend; useuprootfor thetypefor the uproot backend):api_endpoints:-name:<your-endpoint-name>endpoint:<your-endpoint>token:<api-token>type:xaodAll strings are expanded using python'sos.path.expandmethod - so$NAMEand${NAME}will work to expand existing environment variables.You can list multiple end points by repeating the block of dictionary items, but using a different name.Finally, you can create the objectsServiceXAdaptorandMinioAdaptorby hand in your code, passing them as arguments toServiceXDatasetand inject custom endpoints and credentials, avoiding the configuration system. This is probably only useful for advanced users.These config files are used to keep confidential credential information - so that it isn't accidentally placed in a public repository.If no endpoint is specified or config file containing a useful endpoint is found, then the library defaults to the developer endpoint, which ishttp://localhost:5000for the web-service API. No passwords are used in this case.UsageThe following lines will return apandas.DataFramecontaining all the jet pT's from an ATLAS xAOD file containing Z->ee Monte Carlo:fromserviceximportServiceXDatasetquery="(call ResultTTree (call Select (call SelectMany (call EventDataset (list 'localds:bogus')) (lambda (list e) (call (attr e 'Jets') 'AntiKt4EMTopoJets'))) (lambda (list j) (/ (call (attr j 'pt')) 1000.0))) (list 'JetPt') 'analysis' 'junk.root')"dataset="mc15_13TeV:mc15_13TeV.361106.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zee.merge.DAOD_STDM3.e3601_s2576_s2132_r6630_r6264_p2363_tid05630052_00"ds=ServiceXDataset(dataset,backend_name=`xaod`)r=ds.get_data_pandas_df(query)print(r)And the output in a terminal window from running the above script (takes about 1-2 minutes to complete):pythonscripts/run_test.pyhttp://localhost:5000/servicexJetPt
entry038.065707131.96709627.88133736.66958145.624053
......71018342.92614171018430.8157097101856.3480027101865.4727117101875.212714[11355980rowsx1columns]If your query is badly formed or there is an other problem with the backend, an exception will be thrown with information about the error.If you'd like to be able to submit multiple queries and have them run on theServiceXback end in parallel, it is best to use theasynciointerface, which has the identical signature, but is calledget_data_pandas_df_async.For documentation ofget_dataandget_data_asyncsee theservicex.pysource file.Thebackend_nametells the library where to look in theservicex.yamlconfiguration file to find an end point (url and authentication information). See above for more information.How to specify the input dataHow you specify the input data, and what data can be ingested, is ultimately defined by the configuration of theServiceXbackend you are running against. Thisservicexlibrary supports the following:A Dataset Identifier (DID): For example,rucio://mc16a_13TeV:my_dataset, orcernopendata://1507, both of which are resolved to a list of
files (in one case, a set of ATLAS data files, and in the other some CMS Run 1 AOD files).A single file located at ahttporrootendpoint: For example,root://myfile.rootorhttp://myfile.root. ServiceX must be able to
access these files without special permissions.A list of files located athttporrootendpoints: For example,[root://myfile1.root, http://myfile2.root]. ServiceX must be able to
access these files without special permissions.[deprecated] A bare (DID): this is an unadorned identifier, and is routed to the backend's default DID resolver. The default
is defined at runtime. It is deprecated because a backend configuration change can break your code.The Local Data CacheTo speed things up - especially when you run the same query multiple times, theservicexpackage will cache queries data that comes back from Servicex. You can control where this is stored with thecache_pathin the configuration file (see below). By default it is written in the temp directory of your system, under aservicex_{USER}directory. The cache is unbound: it will continuously fill up. You can delete it at any time that you aren't processing data: data will be re-downloaded or re-transformed inServiceX.There are times when you want the system to ignore the cache when it is running. You can do this by usingignore_cache():fromserviceximportignore_cachewithignore_cache():do_query():If you are using a Jupyter notebook, thewithstatement can't really span cells. So useignore_cache().__enter__()instead. Or you can do something like:fromserviceximportignore_cacheic=ignore_cache()ic.__enter__()...ic.__exit__(None,None,None)If you wish to disable the cache for a single dataset, use theignore_cacheparameter when you create it:ds=ServiceXDataset(dataset,ignore_cache=True)Finally, you can ignore the cache for a dataset for a short period of time by using the same context manager pattern:ds=ServiceXData(dataset)withds.ignore_cache():do_query(ds)# Cache is ignoreddo_query(ds)# Cache is not ignoredAnalysis And Query CacheTheservicexlibrary can write out a local file which will map queries to backendrequest-id's. This file can then be used on other people, checked into repositories, etc., to reference the same data in the backend. The advantage is that the backend does not need to re-run the query - theservicexlibrary need only download it again. When a user uses multiple machines or shares analysis code with an analysis team, this is a much more efficient use of resources.By default the library looks for a fileservicex_query_cache.jsonin the current working directory, or a parent directory of the current working directory.To trigger the creation and updating of a cache file call the functionupdate_local_query_cache(). If you like you can pass in a filename/path. By default it will useservicex_query_cache.jsonin the local directory. The file will be both used for look-ups and will be updated with all subsequent queries. Except under very special cases, it is suggested that one users the filenameservicex_query_cache.json.You can also create the file by using the bash commandtouch servicex_query_cache.json- if you are using the default name.If that file is present when a query is run, it will attempt to download the data from the endpoint, only resubmitting the query if the endpoint doesn't know about the query. As long as the fileservicex_query_cache.jsonis in the current working directory (or above), it will be picked up automatically: no need to callupdate_local_query_cache().The cache search order is as follows:The analysis query cache is searched first.If nothing is found there, then the local query cache is used next.If nothing is found there, then the query is resubmitted.Note: Eventually the backends will contain automatic cache lookup and this feature will be much less useful as it will occur automatically, on the backend.Deleting Files from the local Data CacheIt is not recommended to alter the cache. The software expects the cache to be in a certain state, and randomly altering it can lead to unexpected behavior.Besides telling theservicexlibrary to ignore the cache in the above ways, you can also delete files from the local cache.
The local cache directory is split up into sub-directories. Deleting files from each of the directories:query_cache- this directory contains the mapping between the query text (or its hash) and the ServiceX backend'srequest-id. If you delete a file from here, it is as if the query was never made, and is the same as using the ignore methods above.query_cache_status- contains the last retrieved status from the backend. Deleting this will cause the library to refresh the missing status. This file is updated continuously until the query is completed.file_list_cache- Each file contains a json list of all the files in theminiobucket for a particular request id. Deleting a file from this directory will cause the frontend to re-download the complete list of files (the file in this directory isn't created until all files have been downloaded).
-data- This directory contains the files that have been downloaded locally. If you delete a data file from this directory, it will trigger a re-download. Note that if the servicex endpoint doesn't know about the original query, or the minio bucket is missing, it will force the transform being re-run from scratch.ConfigurationTheservicexlibrary searches for configuration information in several locations to determine what end-point it should connect to:The config file can be calledservicex.yaml,servicex.yml, or.servicex. The files are searched in that order, and all present are used.A config file in the current working directory.A config file in any working directory above your current working directory.A config file in the user's home directory ($HOMEon Linux and Mac, and your profile
directory on Windows).Theconfig_defaults.yamlfile distributed with theservicexpackage.The file can contain anapi_endpointas mentioned earlier. In addition the other following things can be put in:cache_path: Location where queries, data, and a record of queries are written. This should be an absolute path the person running the library has r/w access to. On windows, make sure to escape\- and best to follow standardyamlconventions and put the path in quotes - especially if it contains a space. Top level yaml item (don't indent it accidentally!). Defaults to/tmp/servicex_<username>(with the temp directory as appropriate for your platform) Examples:Windows:cache_path: "C:\\Users\\gordo\\Desktop\\cacheme"Linux:cache_path: "/home/servicex-cache"backend_types- a list of yaml dictionaries that contains some defaults for the backends. By default only thereturn_datais there, which forxaodisrootanduprootisparquet. There is also acms_run1_aodwhich returnsroot. Allowsservicexto convert topandas.DataFrameorawkwardif requested by the user.All strings are expanded using python'sos.path.expandmethod - so$NAMEand${NAME}will work to expand existing environment variables.For non-standard use cases, the user can specify:The code generator that is used by the backend. This is done by passing acodegenargument to ServiceXDataset. This argument is normally inherited from the backend type set inservicex.yaml, but can be overridden with any validcodegencontained in the default type listing. Acodegenentry can also be added to a backend in the yaml file to use as default.The type of backend, using thebackend_typeargument on ServiceXDataset. This overrides the backend type setting in theservicex.yamlfile.FeaturesImplemented:Accepts aqastleformatted queryExceptions are used to report back errors of all sorts from the service to the user's code.Data is return in the following forms:pandas.DataFramean in process DataFrame of all the data requestedawkwardan in processJaggedArrayor dictionary ofJaggedArrays.If you haveawkward2.0 installed, then adask_awkwardarray is returned instead.A list of root files that can be opened withuprootand used as desired.Not all output formats are compatible with all transformations.Complete returned data must fit in the process' memoryRun in an async or a non-async environment and non-async methods will accommodate automatically (includingjupyternotebooks).Support up to 100 simultaneous queries from a laptop-like front end without overwhelming the local machine (hopefully ServiceX will be overwhelmed!)Start downloading files as soon as they are ready (before ServiceX is done with the complete transform).It has been tested to run against 100 datasets with multiple simultaneous queries.It supports local caching of query dataIt will provide feedback on progress.Configuration files supported so that user identification information does not have to be checked
into repositories.TestingThis code has been tested in several environments:Windows, Linux, MacOSPython 3.7, 3.8, 3.9, 3.10Jupyter Notebooks (not automated), regular python command-line invoked source filesNon-standard backendsWhen doing backend development, often ports 9000 and 5000 are forwarded to the local machine exposing theminioandServiceX_Appinstances. In that case, you'll need to create a configuration file that hashttp://localhost:5000as the end point. No API token is necessary if the developmentServiceXinstance doesn't have authorization turned on.APIEverything is based around theServiceXDatasetobject. Below is the documentation for the most common parameters.ServiceXDataset(dataset:str,backend_name:Optional[str]=None,image:str='sslhep/servicex_func_adl_xaod_transformer:v0.4',max_workers:int=20,result_destination='object-store',servicex_adaptor:ServiceXAdaptor=None,minio_adaptor:MinioAdaptor=None,cache_adaptor:Optional[Cache]=None,status_callback_factory:Optional[StatusUpdateFactory]=_run_default_wrapper,local_log:log_adaptor=None,session_generator:Callable[[],Awaitable[aiohttp.ClientSession]]=None,config_adaptor:ConfigView=None):'''Create and configure a ServiceX object for a dataset.Argumentsdataset                     Name of a dataset from which queries will be selected.backend_name                The type of backend. Used only if we need to find anend-point. If we do not have a `servicex_adaptor` then thiswill default to xaod, unless you have any endpoint listedin your servicex file. It will default to best match there,in that case.image                       Name of transformer image to use to transform the datamax_workers                 Maximum number of transformers to run simultaneously onServiceX.result_destination          Where the transformers should write the results.Defaults to object-store, but could be used to saveresults to a posix volumeservicex_adaptor            Object to control communication with the servicex instanceat a particular ip address with certain login credentials.Default comes from the `config_adaptor`.minio_adaptor               Object to control communication with the minio servicexinstance.cache_adaptor               Runs the caching for data and queries that are sent up anddown.status_callback_factory     Factory to create a status notification callback for eachquery. One is created per query.local_log                   Log adaptor for logging.session_generator           If you want to control the `ClientSession` object thatis used for callbacks. Otherwise a single one for all`servicex` queries is used.config_adaptor              Control how configuration options are read from theconfiguration file (servicex.yaml, servicex.yml, .servicex).Notes:-  The `status_callback` argument, by default, uses the `tqdm` library to renderprogress bars in a terminal window or a graphic in a Jupyter notebook (with properjupyter extensions installed). If `status_callback` is specified as None, noupdates will be rendered. A custom callback function can also be specified whichtakes `(total_files, transformed, downloaded, skipped)` as an argument. The`total_files` parameter may be `None` until the system knows how many files need tobe processed (and some files can even be completed before that is known).'''To get the data use one of theget_datamethod. They all have the same API, differing only by what they return.|get_data_awkward_async(self,selection_query:str,title:Optional[str]=None)->Dict[bytes,Union[awkward.array.jagged.JaggedArray,numpy.ndarray]]|FetchquerydatafromServiceXmatching`selection_query`andreturnitas|dictionaryofawkwardarrays,anentryforeachcolumn.Thedataisuniquely|ordered(thesamequerywillalwaysreturnthesameorder).Ifspecified,theoptionaltitleispassedtothebackendandcanbeviewedonthestatuspage.||get_data_awkward(self,selection_query:str,title:Optional[str]=None)->Dict[bytes,Union[awkward.array.jagged.JaggedArray,numpy.ndarray]]|FetchquerydatafromServiceXmatching`selection_query`andreturnitas|dictionaryofawkwardarrays,anentryforeachcolumn.Thedataisuniquely|ordered(thesamequerywillalwaysreturnthesameorder).Ifspecified,theoptionaltitleispassedtothebackendandcanbeviewedonthestatuspage.Each data type comes in a pair - anasyncversion and a synchronous version.get_data_awkward_async, get_data_awkward- Returns a dictionary of the requested data asnumpyorJaggedArrayobjects.get_data_rootfiles,get_data_rootfiles_async- Returns a list of locally download files (aspathlib.Pathobjects) containing the requested data. Suitable for opening withROOT::TFileoruproot.get_data_pandas_df,get_data_pandas_df_async- Returns the data as apandasDataFrame. This will fail if the data you've requested has any structure (e.g. is hierarchical, like a single entry for each event, and each event may have some number of jets).get_data_parquet,get_data_parquet_async- Returns a list of files locally downloaded that can be read by any parquet tools.Streaming ResultsTheServiceXbackend generates results file-by-file. The above API will return the list of files when the transform has completed. For large transforms this can take some time: no need to wait until it is completely done before processing the files!get_data_rootfiles_stream,get_data_parquet_stream,get_data_pandas_stream, andget_data_awkward_streamreturn a stream of local file path's as each result from the backend is downloaded. All take just theqastlequery text as a parameter and return a pythonAsyncIteratorofStreamInfoData. Note that files downloaded locally are cached - so when you re-run the same query it will immediately render all theStreamInfoDataobjects from the async stream with no waiting.get_data_rootfiles_url_streamandget_data_parquet_url_streamreturn a stream of URL's that allow direct access in the backend to the data generated as it is finished. All take just theqastlequery text as a parameter, and return a pythonAsyncIteratorofStreamInfoUrl. These methods are probably most useful if you are working in the same data center that theServiceXservice is running in.TheStreamInfoURLcontains abucket,file, and aurlproperty. Theurlproperty can be used to access the requested data without authentication for about 24 hours (depends on theServiceXbackend's configuration). Use thefileto understand what part of the starting dataset that data came from. And as this de-facto points to aminiodatabase currently, thebucketcan be used to find the host bucket name.TheStreamInfoDatacontains afileand apathproperty. Thefileis as above, and thepathis apathlib.Pathobject that points to the file that has been downloaded into the cache locally.An example using the async interface that performs the same operation as the initial example above:fromserviceximportServiceXDatasetquery="(call ResultTTree (call Select (call SelectMany (call EventDataset (list 'localds:bogus')) (lambda (list e) (call (attr e 'Jets') 'AntiKt4EMTopoJets'))) (lambda (list j) (/ (call (attr j 'pt')) 1000.0))) (list 'JetPt') 'analysis' 'junk.root')"dataset="mc15_13TeV:mc15_13TeV.361106.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zee.merge.DAOD_STDM3.e3601_s2576_s2132_r6630_r6264_p2363_tid05630052_00"ds=ServiceXDataset(dataset)asyncforfinds.get_data_rootfiles_stream(query):print(f.path)Notes:ServiceXmight fail part way through the transformation - so be ready for an exception to bubble out of yourAsyncIterator!If you are combining different queries whose filtering is identical, make sure to use thefileproperty to match results - otherwise you won't have an event-to-event matching!DevelopmentFor any changes please feel free to submit pull requests! We are using thegitlabworkflow: themasterbranch represents the latests updates that pass all tests working towards the next version of the software. Any PR's should be based off the most recent version ofmasterif they are for new features. Each release is frozen on a dedicated release branch, e.g. v2.0.0. If a bug fix needs to be applied to an existing release, submit a PR to master mentioning the affected version(s). After the PR is merged to master, it will be applied to the relevant release branch(es) using git cherry-pick.To do development please setup your environment with the following steps:A python 3.7 development environmentFork/Pull down this package, XXpython -m pip install -e .[test]Run the tests to make sure everything is good:pytest.Then add tests as you develop. When you are done, submit a pull request with any required changes to the documentation and the online tests will run.To create a release branchgetcheckout2.0.0
getswitch-cv2.0.0
gitpush