| Total Complexity | 48 |
| Total Lines | 161 |
| Duplicated Lines | 0 % |
Complex classes like browsepy.File often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | #!/usr/bin/env python |
||
| 32 | class File(object): |
||
| 33 | re_charset = re.compile('; charset=(?P<charset>[^;]+)') |
||
| 34 | parent_class = None # none means current class |
||
| 35 | |||
| 36 | def __init__(self, path=None, app=None): |
||
| 37 | self.path = path |
||
| 38 | self.app = current_app if app is None else app |
||
| 39 | |||
| 40 | def remove(self): |
||
| 41 | if not self.can_remove: |
||
| 42 | raise OutsideRemovableBase("File outside removable base") |
||
| 43 | if self.is_directory: |
||
| 44 | shutil.rmtree(self.path) |
||
| 45 | else: |
||
| 46 | os.unlink(self.path) |
||
| 47 | |||
| 48 | def download(self): |
||
| 49 | if self.is_directory: |
||
| 50 | stream = TarFileStream( |
||
| 51 | self.path, |
||
| 52 | self.app.config["directory_tar_buffsize"] |
||
| 53 | ) |
||
| 54 | return Response(stream, mimetype="application/octet-stream") |
||
| 55 | directory, name = os.path.split(self.path) |
||
| 56 | return send_from_directory(directory, name, as_attachment=True) |
||
| 57 | |||
| 58 | def contains(self, filename): |
||
| 59 | return os.path.exists(os.path.join(self.path, filename)) |
||
| 60 | |||
| 61 | def choose_filename(self, filename, attempts=999): |
||
| 62 | new_filename = filename |
||
| 63 | for attempt in range(2, attempts+1): |
||
| 64 | if not self.contains(new_filename): |
||
| 65 | return new_filename |
||
| 66 | new_filename = alternative_filename(filename, attempt) |
||
| 67 | while self.contains(new_filename): |
||
| 68 | new_filename = alternative_filename(filename) |
||
| 69 | return new_filename |
||
| 70 | |||
| 71 | @property |
||
| 72 | def plugin_manager(self): |
||
| 73 | return self.app.extensions['plugin_manager'] |
||
| 74 | |||
| 75 | @property |
||
| 76 | def default_action(self): |
||
| 77 | for action in self.actions: |
||
| 78 | if action.widget.place == 'link': |
||
| 79 | return action |
||
| 80 | endpoint = 'browse' if self.is_directory else 'open' |
||
| 81 | widget = self.plugin_manager.link_class.from_file(self) |
||
| 82 | return self.plugin_manager.action_class(endpoint, widget) |
||
| 83 | |||
| 84 | @cached_property |
||
| 85 | def actions(self): |
||
| 86 | return self.plugin_manager.get_actions(self) |
||
| 87 | |||
| 88 | @cached_property |
||
| 89 | def can_download(self): |
||
| 90 | return self.app.config['directory_downloadable'] or not self.is_directory |
||
| 91 | |||
| 92 | @cached_property |
||
| 93 | def can_remove(self): |
||
| 94 | dirbase = self.app.config["directory_remove"] |
||
| 95 | if dirbase: |
||
| 96 | return self.path.startswith(dirbase + os.sep) |
||
| 97 | return False |
||
| 98 | |||
| 99 | @cached_property |
||
| 100 | def can_upload(self): |
||
| 101 | dirbase = self.app.config["directory_upload"] |
||
| 102 | if self.is_directory and dirbase: |
||
| 103 | return dirbase == self.path or self.path.startswith(dirbase + os.sep) |
||
| 104 | return False |
||
| 105 | |||
| 106 | @cached_property |
||
| 107 | def stats(self): |
||
| 108 | return os.stat(self.path) |
||
| 109 | |||
| 110 | @cached_property |
||
| 111 | def mimetype(self): |
||
| 112 | if self.is_directory: |
||
| 113 | return 'inode/directory' |
||
| 114 | return self.plugin_manager.get_mimetype(self.path) |
||
| 115 | |||
| 116 | @cached_property |
||
| 117 | def is_directory(self): |
||
| 118 | return os.path.isdir(self.path) |
||
| 119 | |||
| 120 | @cached_property |
||
| 121 | def is_file(self): |
||
| 122 | return os.path.isfile(self.path) |
||
| 123 | |||
| 124 | @cached_property |
||
| 125 | def is_empty(self): |
||
| 126 | return not self.raw_listdir |
||
| 127 | |||
| 128 | @cached_property |
||
| 129 | def parent(self): |
||
| 130 | if self.path == self.app.config['directory_base']: |
||
| 131 | return None |
||
| 132 | parent_class = self.parent_class or self.__class__ |
||
| 133 | return parent_class(os.path.dirname(self.path), self.app) |
||
| 134 | |||
| 135 | @cached_property |
||
| 136 | def ancestors(self): |
||
| 137 | ancestors = [] |
||
| 138 | parent = self.parent |
||
| 139 | while parent: |
||
| 140 | ancestors.append(parent) |
||
| 141 | parent = parent.parent |
||
| 142 | return tuple(ancestors) |
||
| 143 | |||
| 144 | @cached_property |
||
| 145 | def raw_listdir(self): |
||
| 146 | return os.listdir(self.path) |
||
| 147 | |||
| 148 | @property |
||
| 149 | def modified(self): |
||
| 150 | return datetime.datetime.fromtimestamp(self.stats.st_mtime).strftime('%Y.%m.%d %H:%M:%S') |
||
| 151 | |||
| 152 | @property |
||
| 153 | def size(self): |
||
| 154 | size, unit = fmt_size(self.stats.st_size, self.app.config["use_binary_multiples"]) |
||
| 155 | if unit == binary_units[0]: |
||
| 156 | return "%d %s" % (size, unit) |
||
| 157 | return "%.2f %s" % (size, unit) |
||
| 158 | |||
| 159 | @property |
||
| 160 | def urlpath(self): |
||
| 161 | return abspath_to_urlpath(self.path, self.app.config['directory_base']) |
||
| 162 | |||
| 163 | @property |
||
| 164 | def name(self): |
||
| 165 | return os.path.basename(self.path) |
||
| 166 | |||
| 167 | @property |
||
| 168 | def type(self): |
||
| 169 | return self.mimetype.split(";", 1)[0] |
||
| 170 | |||
| 171 | @property |
||
| 172 | def encoding(self): |
||
| 173 | if ";" in self.mimetype: |
||
| 174 | match = self.re_charset.search(self.mimetype) |
||
| 175 | gdict = match.groupdict() if match else {} |
||
| 176 | return gdict.get("charset") or "default" |
||
| 177 | return "default" |
||
| 178 | |||
| 179 | def listdir(self): |
||
| 180 | path_joiner = functools.partial(os.path.join, self.path) |
||
| 181 | content = [ |
||
| 182 | self.__class__(path=path_joiner(path), app=self.app) |
||
| 183 | for path in self.raw_listdir |
||
| 184 | ] |
||
| 185 | content.sort(key=lambda f: (f.is_directory, f.name.lower())) |
||
| 186 | return content |
||
| 187 | |||
| 188 | @classmethod |
||
| 189 | def from_urlpath(cls, path, app=None): |
||
| 190 | app = app or current_app |
||
| 191 | base = app.config['directory_base'] |
||
| 192 | return cls(path=urlpath_to_abspath(path, base), app=app) |
||
| 193 | |||
| 448 |