def build_zentao_report(report_type, from_date, to_date, today):
    """生成禅道报告"""
    if today and to_date:
        raise click.BadParameter('参数 --to-date 和 --today 冲突, 只能给其中一个参数赋值')
    elif today and not to_date:
        to_date_str = datetime.now().strftime('%Y-%m-%d')
    elif not today and to_date:
        to_date_str = to_date.strftime('%Y-%m-%d')
    else:
        raise click.MissingParameter('参数 --to-date 或 --today 必填')
    if from_date and report_type:
        raise click.BadParameter(
            '--report-type 和 --from-date 冲突, 选择报告类型后,报告开始日期值将固定')
    elif from_date and not report_type:
        from_date_str = from_date.strftime('%Y-%m-%d')
        reporter = Reporter(from_date_str, to_date_str)
    elif not from_date and report_type == 'daily':
        reporter = DailyReporter(to_date_str)
    elif not from_date and report_type == 'weekly':
        reporter = WeeklyReporter(to_date_str)
    elif not from_date and report_type == 'monthly':
        reporter = MonthlyReporter(to_date_str)
    else:
        raise click.MissingParameter('参数 --from-date 或 --report-type 必填')
    reporter.gen_summary()
    reporter.gen_html_report()
示例#2
0
async def load(
    root: Root,
    sources: Sequence[str],
    destination: Optional[str],
    recursive: bool,
    glob: bool,
    target_directory: Optional[str],
    no_target_directory: bool,
    update: bool,
    progress: bool,
) -> None:
    """
    Copy files and directories using MinIO (EXPERIMENTAL).

    Same as "cp", but uses MinIO and the Amazon S3 protocol.
    """
    target_dir: Optional[URL]
    dst: Optional[URL]
    if update and recursive:
        raise click.UsageError("Cannot use --update and --recursive together")
    if target_directory:
        if no_target_directory:
            raise click.UsageError(
                "Cannot combine --target-directory (-t) and --no-target-directory (-T)"
            )
        if destination is None:
            raise click.MissingParameter(
                param_type="argument", param_hint='"SOURCES..."'
            )
        sources = *sources, destination
        target_dir = parse_file_resource(target_directory, root)
        dst = None
    else:
        if destination is None:
            raise click.MissingParameter(
                param_type="argument", param_hint='"DESTINATION"'
            )
        if not sources:
            raise click.MissingParameter(
                param_type="argument", param_hint='"SOURCES..."'
            )
        dst = parse_file_resource(destination, root)
        if no_target_directory or not await _is_dir(root, dst):
            target_dir = None
        else:
            target_dir = dst
            dst = None

    srcs = await _expand(sources, root, glob, allow_file=True)
    if no_target_directory and len(srcs) > 1:
        raise click.UsageError(f"Extra operand after {str(srcs[1])!r}")

    for src in srcs:
        if target_dir:
            dst = target_dir / src.name
        assert dst
        await cp_s3(
            root, src, dst, recursive=recursive, update=update, progress=progress
        )
示例#3
0
def validate_router_addr(ctx, param, value):
    # If address is not formatted correctly or illegal port is provided
    # etcdClient will complain so no need to worry about it
    if not value:
        print(usage)
        raise click.MissingParameter('--router-addr is missing')
    return value
示例#4
0
def generate(config, target_name):
    """Generate files to be used by build-system"""
    if target_name:
        config.target_name = target_name
    if not hasattr(config, 'target_name'):
        ctx = click.get_current_context()
        raise click.MissingParameter(ctx=ctx, param=ctx.command.params[0])
    else:
        target = Target(config.target_name, config.targets[config.target_name], config.stream_kwargs)
        if target.is_fetch_needed():
            click.echo(
                'Target %s is not deployed, please run "%s deploy --target %s" first.' %
                (config.target_name, PROG_NAME, config.target_name)
            )
            raise click.Abort
        out_dir = generate_plat_cmake(target)
        shutil.copy(
            os.path.join(PAL_PLATFORM_ROOT, 'mbedCloudClientCmake.txt'),
            os.path.join(out_dir, 'CMakeLists.txt')
        )
        click.echo(
            click.style(
                'Generation for %s is successful, please run cmake & make from %s' % (config.target_name, out_dir),
                fg='green'
            )
        )
示例#5
0
def get_default_gateway(ctx):
    """
        Check for a default gateway in config; if not present, check if the user
        only has 1 gateway. If so, use that one.
    """
    name = os.getenv('LAGER_GATEWAY')
    if name is None:
        name = ctx.obj.default_gateway

    if name is None:
        session = ctx.obj.session
        resp = session.list_gateways()
        resp.raise_for_status()
        gateways = resp.json()['gateways']

        if not gateways:
            click.secho(
                'No gateways found! Please contact [email protected]',
                fg='red')
            ctx.exit(1)
        if len(gateways) == 1:
            ctx.obj.default_gateway = gateways[0]['id']
            return gateways[0]['id']

        hint = 'NAME. Set a default using `lager set default gateway <id>`'
        raise click.MissingParameter(
            param=ctx.command.params[0],
            param_hint=hint,
            ctx=ctx,
            param_type='argument',
        )
    return name
    def full_process_value(self, ctx, value):
        value = super(OptionRequiredIf, self).full_process_value(ctx, value)

        if value is None and ctx.params['states_path'] is None:
            msg = 'You need to either give a states file path or a states list'
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value
示例#7
0
def upload(path, file, upload_path, time_out, retry, force):
    if not path and not file:
        raise click.MissingParameter(
            param=click.get_current_context().command.params[2])
    else:
        path_list = set((*file, path))
    commander.upload(path_list, upload_path, time_out, retry, force)
示例#8
0
 def full_process_value(self, ctx, value):
     value = super(OptionRequiredIf, self).full_process_value(ctx, value)
     for v in self._value:
         if value is None and ctx.params[self._option] == v:
             msg = 'Required if --{}={}'.format(self._option, self._value)
             raise click.MissingParameter(ctx=ctx, param=self, message=msg)
     return value
示例#9
0
 def full_process_value(self, ctx, value):
     value = super().full_process_value(ctx, value)
     if ('serial_number' not in ctx.params
             or not ctx.params['serial_number']) and value is None:
         msg = 'Required if "-snr" / "--serial-number" is not defined.'
         raise click.MissingParameter(ctx=ctx, param=self, message=msg)
     return value
示例#10
0
    def full_process_value(self, ctx, value):
        value = super(OptionRequiredIf, self).full_process_value(ctx, value)
        option = self.human_readable_name

        required_params = {
            "webhook": {
                "mutually_exclusive": "function",
                "config":
                ["config_url", "config_secret", "config_insecure_ssl"],
            },
            "function": {
                "mutually_exclusive": "webhook",
                "config": ["config_runtime", "config_code"],
            },
        }
        if value is None:
            expected_params = required_params.get(ctx.params["hook_type"], [])
            for opt in expected_params["config"]:
                if option in expected_params["config"]:
                    if opt not in [key for key in ctx.params.keys()]:
                        msg = f"Required if hook type is {ctx.params['hook_type']}"
                        raise click.MissingParameter(ctx=ctx, message=msg)
            for opt in required_params[
                    expected_params["mutually_exclusive"]]["config"]:
                if opt in [key for key in ctx.params.keys()]:
                    if ctx.params.get(opt) is not None:
                        non_valid_param = opt
                        msg = f"Illegal usage: --{non_valid_param} cannot be used for the hook type {ctx.params['hook_type']}"
                        raise click.UsageError(ctx=ctx, message=msg)

        return value
示例#11
0
def cli(ctx, level, extras, req, output, setup):
    """Calculate requirements for different purposes."""
    if level == 'dev' and not req:
        raise click.UsageError(
            "You must specify --req when using 'dev' level.")
    if not setup and not req:
        raise click.MissingParameter(ctx=ctx,
                                     param_hint='"setup"',
                                     param_type='argument')
    extras = set(req.strip() for extra in extras for req in extra.split(','))

    # figure out the setup.cfg file
    setup_cfg = None

    if setup:
        cfg = os.path.splitext(setup.name)[0] + ".cfg"
        if os.path.exists(cfg):
            setup_cfg = open(cfg, 'r')

    try:
        lines = (
            '{0}\n'.format(req)
            for req in iter_requirements(level, extras, req, setup, setup_cfg))
        output.writelines(lines)
    finally:
        if setup_cfg:
            setup_cfg.close()
示例#12
0
def download(path, file, save_path, share):
    if not path and not file:
        raise click.MissingParameter(param=click.get_current_context().command.params[2])
    else:
        file_list = {*file, path}

    commander.download(file_list, save_path, share=share)
示例#13
0
def validate_ip(ctx, param, value):
    if not value:
        print(usage)
        raise click.MissingParameter('--internal-addr is missing')
    if len(value.split('/')) != 2:
        print(usage)
        raise click.BadParameter('--internal-addr illegal address provided')
    port = value.split('/')[1]
    addr = value.split('/')[0]
    if len(addr.split('.')) == 4:
        print(usage)
        raise click.BadParameter('--internal-addr illegal address provided')
    try:
        port = int(port)
    except:
        print(usage)
        raise click.BadParameter('--internal-addr illegal address provided')

    if port >= 32 or port <= 0:
        print(usage)
        raise click.BadParameter('--internal-addr illegal address provided')

    for octet in addr.split('.'):
        try:
            octet = int(octet)
            if octet >= 256 or octet < 0:
                raise Exception
        except:
            print(usage)
            raise click.BadParameter(
                '--internal-addr illegal address provided')
    return value
示例#14
0
def main(fastq, output, kind, log_length, bam, downsample):
    """A package for sanity checking (quality control) your long read data.
        Feed it a fastq file and in return you will receive a PDF with four plots:\n
            1. GC content histogram with distribution curve for sample.\n
            2. Jointplot showing the read length vs. phred quality score for each
            read. The interior representation of this plot can be altered with the
            --kind option.\n
            3. Box plot of the phred quality score at positional bins across all reads. The reads are binned into read positions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11-20, 21-50, 51-100, 101-200, 201-300. Plots from the start of reads.\n
            4. Same as 3, but plots from the end of the read.\n
    Additionally, if you provide a BAM/SAM file a histogram of the read percent
    identity will be added to the report.
    """
    if not any([fastq, bam]):
        raise click.MissingParameter("Either --fastq, --bam or both must be "
                                     "given as arguments.")
    sns.set(style=SEABORN_STYLE)

    # if the specified output is a directory, default pdf name is fastq name.
    if os.path.isdir(output):
        # get the basename of the fastq file and add pdf extension
        basename, ext = os.path.splitext(os.path.basename(fastq or bam))
        # if file is gzipped, need to also strip fastq extension
        if ext == '.gz':
            basename = os.path.splitext(os.path.basename(basename))[0]

        filename = basename + REQUIRED_EXT
        save_as = os.path.join(output, filename)
    else:  # if file name is provided in output, make sure it has correct ext.
        extension = os.path.splitext(output)[-1]
        if extension.lower() != REQUIRED_EXT:
            save_as = output + REQUIRED_EXT
        else:
            save_as = output

    plots_for_report = []
    if fastq:
        with pysam.FastxFile(fastq) as fastq_file:
            # collect the data needed for plotting
            (gc_content,
             read_lengths,
             mean_quality_scores,
             bins_from_start,
             bins_from_end) = utils.collect_fastq_data(fastq_file, downsample)

        # generate plots
        plots_for_report.extend([
            plots.gc_plot(gc_content),
            plots.length_vs_qual_plot(read_lengths, mean_quality_scores,
                                      log_length=log_length, kind=kind),
            plots.quality_per_position(bins_from_start, 'start'),
            plots.quality_per_position(bins_from_end, 'end')
        ])
    if bam:
        # generate read percent identity plot
        perc_identities = utils.sam_percent_identity(bam, downsample)
        plots_for_report.append(plots.percent_identity(perc_identities))

    plots.save_plots_to_pdf(plots_for_report, save_as)

    return 0
示例#15
0
def transfer_ownership(general_config, actor_options, target_address, gas):
    """Transfer ownership of contracts to another address."""
    emitter = general_config.emitter
    ADMINISTRATOR, _, _, _ = actor_options.create_actor(emitter)

    if not target_address:
        target_address = click.prompt(PROMPT_NEW_OWNER_ADDRESS, type=EIP55_CHECKSUM_ADDRESS)

    contract_name = actor_options.contract_name
    if not contract_name:
        raise click.MissingParameter(param="--contract-name", message="You need to specify an ownable contract")

    try:
        contract_deployer_class = ADMINISTRATOR.deployers[contract_name]
    except KeyError:
        message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name,
                                               contracts=ADMINISTRATOR.ownable_deployer_classes.keys())
        emitter.echo(message, color='red', bold=True)
        raise click.Abort()

    if contract_deployer_class not in ADMINISTRATOR.ownable_deployer_classes:
        message = CONTRACT_IS_NOT_OWNABLE.format(contract_name=contract_name)
        emitter.echo(message, color='red', bold=True)
        raise click.Abort()

    contract_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry,
                                                deployer_address=ADMINISTRATOR.deployer_address)
    receipt = contract_deployer.transfer_ownership(new_owner=target_address, transaction_gas_limit=gas)
    paint_receipt_summary(emitter=emitter, receipt=receipt)
示例#16
0
文件: BAGEL.py 项目: niekwit/bagel
    def full_process_value(self, ctx, value):
        value = super(OptionRequiredIf, self).full_process_value(ctx, value)

        if value is None and ctx.params['filter_multi_target'] is True:
            msg = 'Error! Multi-target-filtering selected and not align-info provided.\n' \
                  'Please indicate align-info file.'
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value
示例#17
0
def check_bad_option_usage(filepath, plugins, project_dir, output_dir):
    """Validates the arguments passed to the Obfuscator"""
    if filepath and project_dir:
        raise click.BadOptionUsage('project_dir',
                                   'You cannot provide a filepath and a project directory')
    elif not filepath and not project_dir and (output_dir == '.'):
        raise click.MissingParameter(
            'You must provide some arguments to obscure')
示例#18
0
    def full_process_value(self, ctx, value):
        value = super(EnsureSaveState, self).full_process_value(ctx, value)

        if value is None and (ctx.params['save_state'] == 'remote'
                              or ctx.params['save_state'] == 'all'):
            msg = 'Required if save_state = [remote | all]'
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value
示例#19
0
def upload(path, file, upload_path, time_out, retry, force, share, chunk_size,
           c):
    if not path and not file:
        raise click.MissingParameter(
            param=click.get_current_context().command.params[2])
    else:
        path_list = set(filter(None, {*file, path}))
    commander.upload(path_list, upload_path, time_out, retry, force, share,
                     chunk_size, c)
示例#20
0
def show(config, date, start, end, full, weekday):
    """
    Show loaded hours
    """
    start = date if date else start
    end = date if date else end
    if end and not start:
        raise click.MissingParameter(f'End date requires a starting date')
    show_hours(config, start, end, full, weekday)
示例#21
0
def share(path, file, file_id, expire_sec, share_link, download_link, save,
          share_official):
    if not path and not file and not file_id:
        raise click.MissingParameter(
            param=click.get_current_context().command.params[2])
    else:
        path_list = list(filter(None, {*file, path}))
        file_id_list = list(filter(None, {*file_id}))
    if share_official:
        commander.share_link(path_list, file_id_list, expire_sec)
    elif path_list:
        for path in path_list:
            if path:
                commander.share(path, expire_sec, share_link, download_link,
                                save)
    else:
        raise click.MissingParameter(
            param=click.get_current_context().command.params[1])
示例#22
0
    def full_process_value(self, ctx, value):
        value = super(EnsureLifecycleTeardownRelease,
                      self).full_process_value(ctx, value)

        if value is None and (
                ctx.params['action'] == LIFECYCLE_ACTIONS.TEARDOWN.value
                or ctx.params['action'] == LIFECYCLE_ACTIONS.RELEASE.value):
            msg = 'Required if action = [teardown | release]'
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value
示例#23
0
 def full_process_value(self, ctx, value):
     try:
         value = super().full_process_value(ctx, value)
         if self.required_fn and self.value_is_missing(value):
             if self.is_required(ctx):
                 raise click.MissingParameter(ctx=ctx, param=self)
     except click.MissingParameter:
         if self.is_required(ctx):
             raise
     return value
示例#24
0
def pythonpath(config, action, env, source_env):
    if action in modify_actions and action != "clear" and not env:
        raise click.MissingParameter("error: missing env")
    p = PythonPath(config=config, source_env=source_env)
    if action == "show":
        print(json.dumps(p.external_site_packages, indent=2))
    elif action == "infer":
        p.infer()
    else:
        p.modify(action, env)
示例#25
0
def escrow(general_config: GroupGeneralConfig,
           worklock_options: WorkLockOptions,
           force: bool,
           hw_wallet: bool,
           value: Decimal):
    """Create an ETH escrow, or increase an existing escrow"""
    emitter, registry, blockchain = worklock_options.setup(general_config=general_config)
    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    now = maya.now().epoch
    if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date:
        emitter.echo(BIDDING_WINDOW_CLOSED, color='red')
        raise click.Abort()

    _bidder_address = worklock_options.get_bidder_address(emitter, registry)
    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    existing_bid_amount = bidder.get_deposited_eth
    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")

        if not existing_bid_amount:  # It's the first bid
            minimum_bid = bidder.worklock_agent.minimum_allowed_bid
            minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether')
            prompt = BID_AMOUNT_PROMPT_WITH_MIN_BID.format(minimum_bid_in_eth=minimum_bid_in_eth)
        else:  # There's an existing bid and the bidder is increasing the amount
            emitter.message(EXISTING_BID_AMOUNT_NOTICE.format(eth_amount=Web3.fromWei(existing_bid_amount, 'ether')))
            minimum_bid_in_eth = Web3.fromWei(1, 'ether')
            prompt = BID_INCREASE_AMOUNT_PROMPT
        value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth))

    value = int(Web3.toWei(Decimal(value), 'ether'))

    if not force:
        if not existing_bid_amount:
            paint_bidding_notice(emitter=emitter, bidder=bidder)
            click.confirm(f"Place WorkLock escrow of {prettify_eth_amount(value)}?", abort=True)
        else:
            click.confirm(f"Increase current escrow ({prettify_eth_amount(existing_bid_amount)}) "
                          f"by {prettify_eth_amount(value)}?", abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Escrow...")

    maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked)
    available_claim = NU.from_nunits(bidder.available_claim)
    message = f'\nCurrent escrow: {prettify_eth_amount(bidder.get_deposited_eth)} | Allocation: {available_claim}\n'
    if available_claim > maximum:
        message += f"\nThis allocation is currently above the allowed max ({maximum}), " \
                   f"so the escrow may be partially refunded.\n"
    message += f'Note that the available allocation value may fluctuate until the escrow period closes and ' \
               f'allocations are finalized.\n'
    emitter.echo(message, color='yellow')

    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
示例#26
0
def bid(general_config, worklock_options, registry_options, force, hw_wallet, value):
    """Place a bid, or increase an existing bid"""
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)

    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    now = maya.now().epoch
    if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date:
        raise click.Abort(f"You can't bid, the bidding window is closed.")

    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=registry_options.provider_uri,
                                                                poa=registry_options.poa,
                                                                network=registry_options.network,
                                                                registry=registry,
                                                                show_balances=True)

    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")

        existing_bid_amount = bidder.get_deposited_eth
        if not existing_bid_amount:  # It's the first bid
            minimum_bid = bidder.worklock_agent.minimum_allowed_bid
            minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether')
            prompt = f"Enter bid amount in ETH (at least {minimum_bid_in_eth} ETH)"
        else:  # There's an existing bid and the bidder is increasing the amount
            emitter.message(f"You have an existing bid of {Web3.fromWei(existing_bid_amount, 'ether')} ETH")
            minimum_bid_in_eth = Web3.fromWei(1, 'ether')
            prompt = f"Enter the amount in ETH that you want to increase your bid"
        value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth))

    value = int(Web3.toWei(Decimal(value), 'ether'))

    if not force:
        paint_bidding_notice(emitter=emitter, bidder=bidder)
        click.confirm(f"Place WorkLock bid of {prettify_eth_amount(value)}?", abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Bid...")

    maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked)
    available_claim = NU.from_nunits(bidder.available_claim)
    message = f'Current bid: {prettify_eth_amount(bidder.get_deposited_eth)} | Claim: {available_claim}\n'
    if available_claim > maximum:
        message += f"This claim is currently above the allowed max ({maximum}), so the bid may be partially refunded.\n"
    message += f'Note that available claim value may fluctuate until bidding closes and claims are finalized.\n'
    emitter.echo(message, color='yellow')

    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
示例#27
0
    def _consume_metadata(self, ctx, opts):
        # double consume
        md_file = super().consume_value(ctx, opts)
        # consume uses self.name, so mutate but backup for after
        backup, self.name = self.name, self.q2_extra_dest
        md_col = super().consume_value(ctx, opts)
        self.name = backup

        if (md_col is None) != (md_file is None):
            # missing one or the other
            if md_file is None:
                raise click.MissingParameter(ctx=ctx, param=self)
            else:
                raise click.MissingParameter(param_hint=self.q2_extra_opts,
                                             ctx=ctx,
                                             param=self)

        if md_col is None and md_file is None:
            return None
        else:
            return (md_file, md_col)
示例#28
0
文件: user.py 项目: zuzhi/rssant
def redeem_code_exchange(user: str, userfile: io.FileIO, code: str):
    if not user and not userfile:
        raise click.MissingParameter('user or userfile required')
    user_s = []
    if user:
        user_s.append(user)
    if userfile:
        for line in userfile.read().splitlines():
            line = line.strip().split()[0]
            if line:
                user_s.append(line)
    for user in user_s:
        _redeem_code_exchange(user, code)
示例#29
0
def upload(ctx, config, input, name, endpoint):
    """Create file from an input in the bucket."""
    bucket_id = ctx.obj.get('bucket_id')

    if bucket_id is None:
        raise click.MissingParameter('bucket has to be defined',
                                     ctx=ctx,
                                     param_hint='bucket_id')

    client = from_config(config, endpoint=endpoint)
    bucket = client.buckets[bucket_id]
    try:
        name = name or input.name
    except AttributeError:
        raise click.MissingParameter('name has to be define when using STDIN',
                                     ctx=ctx,
                                     param_hint='name')

    with bucket.files.open(name, 'w') as fp:
        fp.write(input.read())

    click.echo(fp.id)
示例#30
0
def cli(ctx, level, extras, req, output, setup):
    """Calculate requirements for different purposes."""
    if level == 'dev' and not req:
        raise click.UsageError(
            "You must specify --req when using 'dev' level.")
    if not setup and not req:
        raise click.MissingParameter(ctx=ctx,
                                     param_hint='"setup"',
                                     param_type='argument')
    extras = set(req.strip() for extra in extras for req in extra.split(','))

    lines = ('{0}\n'.format(req)
             for req in iter_requirements(level, extras, req, setup))
    output.writelines(lines)