Coverage for awsutils / aws_s3_upload.py: 94%

83 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-21 13:47 +0900

1# -*- config: utf-8 -*- 

2'''aws_s3_upload module. 

3 

4Copyright ycookjp 

5https://github.com/ycookjp/ 

6 

7''' 

8 

9import boto3 

10import logging 

11import os 

12import shutil 

13import sys 

14import yaml 

15 

16class AWSS3MoveConfig(): 

17 region_name = '' 

18 ''' 

19 リージョン名 

20 ''' 

21 access_key_id = None 

22 ''' 

23 IAMユーザーのアクセスキー 

24 ''' 

25 secret_access_key = None 

26 ''' 

27 IAMユーザーのシークレットアクセスキー 

28 ''' 

29 

30def _load_config(script_path: str): 

31 ''' 

32  

33 Loads configuration file with YAMS format.  

34 Configuration file should be located at same directory of specified 

35 script path, and name should be base name of this script except for 

36 extension is '.yml'. 

37  

38 Args: 

39 script_path (str): script path 

40  

41 Returns: 

42 AWSS3MoveConfig: Returns configuration object. 

43  

44 ''' 

45 config_path = os.path.splitext(script_path)[0] + '.yml' 

46 with open(config_path, 'r', encoding='utf-8') as file: 

47 yaml_conf = yaml.load(file, Loader=yaml.SafeLoader) 

48 config = AWSS3MoveConfig() 

49 config.region_name = yaml_conf['region_name'] 

50 config.access_key_id = yaml_conf.get('access_key_id') 

51 config.secret_access_key = yaml_conf.get('secret_access_key') 

52 

53 return config 

54 

55def _operation_log(file_path: str, s3bucket_name: str, s3key: str, 

56 remove_src: bool): 

57 ''' 

58  

59 ファイルのアップロード/移動のログを出力します。 

60  

61 Arts: 

62 file_path (str): アップロード/移動したファイルのパス 

63 s3bucket_name (str): アップロード/移動先のS3バケット名 

64 s3key (str): アップロード/移動先のキー(パス月乃ファイル名) 

65 remove_src (bool): ファイルを移動した場合はTrue 

66  

67 ''' 

68 action = 'uploaded' 

69 if remove_src: 

70 action = 'moved' 

71 

72 logging.info(f"{action} {file_path} to s3://{s3bucket_name}/{s3key}") 

73 

74def _remove_files(file_path: str): 

75 ''' 

76  

77 指定されたファイル、または指定されたディレクトリ配下のファイル・ディレクトリ 

78 を削除します。 

79  

80 Args: 

81 file_path (str): 削除対象のファイルまたはディレクトリのパス 

82  

83 ''' 

84 if os.path.isfile(file_path): 

85 os.remove(file_path) 

86 elif os.path.isdir(file_path): 86 ↛ exitline 86 didn't return from function '_remove_files' because the condition on line 86 was always true

87 for name in os.listdir(file_path): 

88 name_path = os.path.join(file_path, name) 

89 if os.path.isfile(name_path): 

90 os.remove(name_path) 

91 elif os.path.isdir(name_path): 91 ↛ 87line 91 didn't jump to line 87 because the condition on line 91 was always true

92 shutil.rmtree(name_path) 

93 

94def _get_s3_key(s3_folder_path: str, top_dir: str, file_path: str): 

95 ''' 

96  

97 S3バケットのフォルダ名と、アップロード対象ファイルの最上位パスからの 

98 相対パスを使用して、S3のキーを取得します。 

99  

100 Args: 

101 s3_folder_path (str): S3バケットのフォルダの名前。サブフォルダの階層を 

102 持つ場合は、フォルダを'/'で区切って指定する。 

103 top_dir (str): アップロード対象のファイルが格納されているディレクトリの 

104 最上位ディレクトリ。 

105 file_path (str): アップロード対象のファイルのパス。 

106 Returns: 

107 str: s3パケットのフォルダ名の後ろに、アップロード対象ファイルの 

108 最上位ディレクトリからの相対パスを'/'で連結した文字列を返します。 

109  

110 ''' 

111 s3key = file_path 

112 

113 if top_dir != None and len(top_dir) > 0 and file_path.index(top_dir) == 0: 113 ↛ 116line 113 didn't jump to line 116 because the condition on line 113 was always true

114 s3key = s3key[len(top_dir):len(s3key)] 

115 

116 if s3key[0] == os.sep: 

117 s3key = s3key[1:len(s3key)] 

118 

119 if os.sep != '/': 

120 s3key = s3key.replace(os.sep, '/') 

121 

122 if s3_folder_path != None and len(s3_folder_path) > 0: 

123 s3key = s3_folder_path + '/' + s3key 

124 

125 return s3key 

126 

127def upload_file(file_or_dir_path: str, s3bucket_name: str, s3folder_path: str=None, 

128 remove_src:bool=False, script_path:str=__file__, 

129 access_key_id:str=None, secret_access_key:str=None): 

130 ''' 

131  

132 指定されたファイル、または指定されたディレクトリを再帰的に探して取得した 

133 ファイルを指定されたS3バケットのs3フォルダの下にアップロードします。 

134 この関数は、引数で指定されたスクリプトパスと同じディレクトリに存在する 

135 設定ファイル(スクリプトファイルと同じディレクトリに配置された、 

136 スクリプト名の拡張子が'.yml'であるファイル)から、以下の情報を取得して 

137 AWSのS3サービスに接続します。 

138  

139 * region_name - リージョン名 

140 * access_key_id - (オプション) IAMユーザーのアクセスキー。引数にアクセスキーが 

141 指定されなかった場合は、設定ファイルから取得したものを使用する。 

142 * secret_access_key - (オプション) IAMユーザーのシークレットキー。引数に 

143 シークレットキーが指定されなかった場合は、設定ファイルから取得した 

144 ものを使用する。 

145  

146 Args: 

147 file_or_dir_path (str): アップロード対象のファイルのパス、または 

148 ファイルを配置したディレクトリを指定します。 

149 s3bucket_name (str): アップロード先のS3バケット名を指定します。 

150 s3folder_path (str, optional): アップロード先のS3フォルダ名を 

151 指定します。S3フォルダがサブフォルダの場合は、フォルダの間を'/'で 

152 区切ります。この引数を省略するとフォルダ名を付けずにアップロード 

153 します。 

154 remove_src (bool, optional): ファイルをアップロード後に 

155 元のファイルを削除する場合はTrue、削除しない場合はFalseを指定 

156 します。Trueを指定した場合は、アップロード対象がファイルの場合は 

157 そのファイルを、ディレクトリの場合は、そのディレクトリ配下の 

158 ファイル・ディレクトリを削除します。 

159 script_path (str, optional): スクリプトのパス 

160 access_key_id (str, optional): IAMユーザーのアクセスキー 

161 secret_access_key (str, optional): IAMユーザーのシークレットキー 

162  

163 Raises: 

164 Exception: file_or_dir_path で指定したパスがファイルでも 

165 ディレクトリでも無い場合 

166  

167 ''' 

168 config = _load_config(script_path) 

169 if access_key_id == None: 169 ↛ 171line 169 didn't jump to line 171 because the condition on line 169 was always true

170 access_key_id = config.access_key_id 

171 if secret_access_key == None: 171 ↛ 174line 171 didn't jump to line 174 because the condition on line 171 was always true

172 secret_access_key = config.secret_access_key 

173 

174 s3_client = boto3.client('s3', aws_access_key_id=access_key_id, 

175 aws_secret_access_key=secret_access_key) 

176 

177 if os.path.isfile(file_or_dir_path): 

178 file_path = file_or_dir_path 

179 s3key = _get_s3_key(s3folder_path, os.path.dirname(file_path), file_path) 

180 s3_client.upload_file(file_path, s3bucket_name, s3key) 

181 _operation_log(file_path, s3bucket_name, s3key, remove_src) 

182 elif os.path.isdir(file_or_dir_path): 

183 topdir = os.path.abspath(file_or_dir_path) 

184 for dirpath, dirnames, filenames in os.walk(topdir): 

185 filenames.sort() 

186 for filename in filenames: 

187 file_path = os.path.join(dirpath, filename) 

188 s3key = _get_s3_key(s3folder_path, topdir, file_path) 

189 s3_client.upload_file(file_path, s3bucket_name, s3key) 

190 _operation_log(file_path, s3bucket_name, s3key, remove_src) 

191 else: 

192 raise Exception('Not found source file or directory') 

193 

194 if remove_src: 

195 _remove_files(file_or_dir_path) 

196 

197def main(args): 

198 file_or_dir_path = args[1] 

199 s3bucket_name = args[2] 

200 s3folder_path = args[3] 

201 remove_src = False 

202 if len(args) > 4: 

203 val = args[4].lower() 

204 remove_src = val == 'true' 

205 

206 upload_file(file_or_dir_path, s3bucket_name, s3folder_path, remove_src) 

207 

208if __name__ == '__main__': 208 ↛ 209line 208 didn't jump to line 209 because the condition on line 208 was never true

209 main(sys.argv)