def extract_bower_zipfile(self, zip_file, dest, expected_version=None):
        bower_json = None
        root = None
        deps_installed = []
        for info in zip_file.infolist():
            if PurePath(info.filename).name == "bower.json":
                with zip_file.open(info) as f:
                    bower_json = json.load(f)
                    root = str(PurePath(info.filename).parent)
                break
        version = bower_json["version"]
        if expected_version is not None:
            expected_version = Bower.clean_semver(expected_version)
            if not semver.match(version, expected_version):
                click.secho("error: versions do not match ({} =/= {})".format(
                    version, expected_version))
                raise InvalidPackageError
        if "dependencies" in bower_json:
            for package, version in bower_json["dependencies"].items():
                url = Bower.get_package_url(package)
                deps_installed.extend(self.get_bower_package(
                    url, dest=dest, version=version))
        ignore_patterns = [GitIgnorePattern(ig) for ig in bower_json["ignore"]]
        path_spec = PathSpec(ignore_patterns)
        namelist = [path for path in zip_file.namelist()
                    if PurePath(path).parts[0] == root]
        ignored = list(path_spec.match_files(namelist))
        for path in namelist:
            dest_path = PurePath(
                bower_json["name"],
                *PurePath(path).parts[1:])

            if path in ignored:
                continue

            for path in ignored:
                for parent in PurePath(path):
                    if parent in ignored:
                        continue

            if path.endswith("/"):
                if list(path_spec.match_files([str(dest_path)])):
                    ignored.append(PurePath(path))
                elif not (dest / dest_path).is_dir():
                    (dest / dest_path).mkdir(parents=True)
            else:
                target_path = dest / dest_path.parent / dest_path.name
                source = zip_file.open(path)
                target = target_path.open("wb")
                with source, target:
                    shutil.copyfileobj(source, target)
        deps_installed.append((bower_json["name"], bower_json["version"]))
        return deps_installed
def label_line(ax, X, Y, U, V, label, color='k', size=8):
        """Add a label to a line, at the proper angle.

        Arguments
        ---------
        line : matplotlib.lines.Line2D object,
        label : str
        x : float
        x-position to place center of text (in data coordinated
        y : float
        y-position to place center of text (in data coordinates)
        color : str
        size : float
        """

        x1, x2 = X, X + U
        y1, y2 = Y, Y + V

        if y2 == 0:
            y2 = y1
        if x2 == 0:
            x2 = x1

        x = (x1 + x2) / 2
        y = (y1 + y2) / 2

        slope_degrees = np.rad2deg(np.angle(U + V * 1j))
        if slope_degrees < 0:
            slope_degrees += 180
        if 90 < slope_degrees <= 270:
            slope_degrees += 180

        x_offset = np.sin(np.deg2rad(slope_degrees))
        y_offset = np.cos(np.deg2rad(slope_degrees))
        bbox_props = dict(boxstyle="Round4, pad=0.1", fc="white", lw=0)
        text = ax.annotate(label, xy=(x, y), xytext=(x_offset * 10, y_offset * 8),
                           textcoords='offset points',
                           size=size, color=color,
                           horizontalalignment='center',
                           verticalalignment='center',
                           fontfamily='monospace', fontweight='bold', bbox=bbox_props)

        text.set_rotation(slope_degrees)
        return text