@@ -1883,3 +1883,156 @@ def __delitem__(self, key):
1883
1883
with self ._mutex :
1884
1884
self ._invalidate_keys ()
1885
1885
self ._invalidate_value (key )
1886
+
1887
+
1888
+ class ABSStore (MutableMapping ):
1889
+ """Storage class using Azure Blob Storage (ABS).
1890
+
1891
+ Parameters
1892
+ ----------
1893
+ container_name : string
1894
+ The name of the ABS container to use. Currently this must exist in the
1895
+ storage account.
1896
+ prefix : string
1897
+ Location of the "directory" to use as the root of the storage hierarchy
1898
+ within the container.
1899
+ account_name : string
1900
+ The Azure blob storage account name.
1901
+ account_key : string
1902
+ The Azure blob storage account acess key.
1903
+
1904
+ Notes
1905
+ -----
1906
+ In order to use this store, you must install the Azure Blob Storage
1907
+ `Python Client Library <https://github.com/Azure/azure-storage-python/tree/master/azure-storage-blob>`_ version >= 1.3.0.
1908
+ """
1909
+
1910
+ def __init__ (self , container_name , prefix , account_name , account_key ):
1911
+ self .account_name = account_name
1912
+ self .account_key = account_key
1913
+ self .container_name = container_name
1914
+ self .prefix = normalize_storage_path (prefix )
1915
+ self .initialize_container ()
1916
+
1917
+ def initialize_container (self ):
1918
+ from azure .storage .blob import BlockBlobService
1919
+ self .client = BlockBlobService (self .account_name , self .account_key )
1920
+
1921
+ # needed for pickling
1922
+ def __getstate__ (self ):
1923
+ state = self .__dict__ .copy ()
1924
+ return state
1925
+
1926
+ def __setstate__ (self , state ):
1927
+ self .__dict__ .update (state )
1928
+ self .initialize_container ()
1929
+
1930
+ def __enter__ (self ):
1931
+ return self
1932
+
1933
+ def __exit__ (self , * args ):
1934
+ pass
1935
+
1936
+ def _append_path_to_prefix (path , prefix ):
1937
+ return '/' .join ([normalize_storage_path (prefix ),
1938
+ normalize_storage_path (path )])
1939
+
1940
+ def full_path (self , path = None ):
1941
+ return _append_path_to_prefix (path , self .prefix )
1942
+
1943
+ def __getitem__ (self , key ):
1944
+ blob_name = '/' .join ([self .prefix , key ])
1945
+ blob = self .client .get_blob_to_bytes (self .container_name , blob_name )
1946
+ if blob :
1947
+ return blob .content
1948
+ else :
1949
+ raise KeyError ('Blob %s not found' % blob_name )
1950
+
1951
+ def __setitem__ (self , key , value ):
1952
+ blob_name = '/' .join ([self .prefix , key ])
1953
+ self .client .create_blob_from_text (self .container_name , blob_name , value )
1954
+
1955
+ def __delitem__ (self , key ):
1956
+ raise NotImplementedError
1957
+
1958
+ def __eq__ (self , other ):
1959
+ return (
1960
+ isinstance (other , ABSStore ) and
1961
+ self .container_name == other .container_name and
1962
+ self .prefix == other .prefix
1963
+ )
1964
+
1965
+ def keys (self ):
1966
+ raise NotImplementedError
1967
+
1968
+ def __iter__ (self ):
1969
+ raise NotImplementedError
1970
+
1971
+ def __len__ (self ):
1972
+ raise NotImplementedError
1973
+
1974
+ def __contains__ (self , key ):
1975
+ blob_name = '/' .join ([self .prefix , key ])
1976
+ if self .client .exists (self .container_name , blob_name ):
1977
+ return True
1978
+ else :
1979
+ return False
1980
+
1981
+ def list_abs_directory_blobs (self , prefix ):
1982
+ """Return list of all blobs from an abs prefix."""
1983
+ return [blob .name for blob in self .client .list_blobs (self .container_name )]
1984
+
1985
+ def list_abs_subdirectories (self , prefix ):
1986
+ """Return list of all "subdirectories" from an abs prefix."""
1987
+ return list (set ([blob .name .rsplit ('/' , 1 )[0 ] for blob in self .client .list_blobs (self .container_name ) if '/' in blob .name ]))
1988
+
1989
+ def _strip_prefix_from_path (path , prefix ):
1990
+ # normalized things will not have any leading or trailing slashes
1991
+ path_norm = normalize_storage_path (path )
1992
+ prefix_norm = normalize_storage_path (prefix )
1993
+ if path_norm .startswith (prefix_norm ):
1994
+ return path_norm [(len (prefix_norm )+ 1 ):]
1995
+ else :
1996
+ return path
1997
+
1998
+ def list_abs_directory (self , prefix , strip_prefix = True ):
1999
+ """Return a list of all blobs and subdirectories from an abs prefix."""
2000
+ items = set ()
2001
+ items .update (self .list_abs_directory_blobs (prefix ))
2002
+ items .update (self .list_abs_subdirectories (prefix ))
2003
+ items = list (items )
2004
+ if strip_prefix :
2005
+ items = [_strip_prefix_from_path (path , prefix ) for path in items ]
2006
+ return items
2007
+
2008
+ def dir_path (self , path = None ):
2009
+ store_path = normalize_storage_path (path )
2010
+ # prefix is normalized to not have a trailing slash
2011
+ dir_path = self .prefix
2012
+ if store_path :
2013
+ dir_path = os .path .join (dir_path , store_path )
2014
+ else :
2015
+ dir_path += '/'
2016
+ return dir_path
2017
+
2018
+ def listdir (self , path = None ):
2019
+ dir_path = self .dir_path (path )
2020
+ return sorted (self .list_abs_directory (dir_path , strip_prefix = True ))
2021
+
2022
+ def rename (self , src_path , dst_path ):
2023
+ raise NotImplementedErrror
2024
+
2025
+ def rmdir (self , path = None ):
2026
+ dir_path = normalize_storage_path (self .full_path (path )) + '/'
2027
+ for blob in self .client .list_blobs (self .container_name , prefix = dir_path ):
2028
+ self .client .delete_blob (self .container_name , blob .name )
2029
+
2030
+ def getsize (self , path = None ):
2031
+ dir_path = self .dir_path (path )
2032
+ size = 0
2033
+ for blob in self .client .list_blobs (self .container_name , prefix = dir_path ):
2034
+ size += blob .properties .content_length
2035
+ return size
2036
+
2037
+ def clear (self ):
2038
+ raise NotImplementedError
0 commit comments