🚀 جرب Zilliz Cloud، الـ Milvus المدارة بالكامل، مجاناً — تجربة أداء أسرع بـ 10 أضعاف! جرب الآن>>

milvus-logo
LFAI

نظرة عامة

  • Engineering
September 15, 2021
Zhen Chen

نظرة عامة

عنوان URL للمشروع: https://github.com/milvus-io/milvus_cli

التحضير Python3.8, Click 8.0.x

تجميع الأوامر

إنشاء أمر

import click
from utils import PyOrm

@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
    """Milvus CLI"""
    ctx.obj = PyOrm() # PyOrm is a util class which wraps the milvus python SDK. You can pass any class instance here. Any command function passed by @click.obj can call it.

if __name__ == '__main__':
    cli()

كما في الكود أعلاه، نستخدم @click.group() لإنشاء مجموعة أوامر cli كنقطة دخول. لتنفيذ CLI موجه نحتاج إلى تعطيل رسائل المساعدة للإدخال، لذلك نضيف no_args_is_help=False و add_help_option=False و invoke_without_command=True. ولن تتم طباعة أي شيء إذا قمنا بإدخال cli في المحطة الطرفية فقط.

إلى جانب ذلك نستخدم @click.pass_context لتمرير سياق إلى هذه المجموعة لمزيد من الاستخدام.

إنشاء أمر فرعي لمجموعة الأوامر

ثم نضيف الأمر الفرعي الأول help تحت cli:

# Print the help message of specified command.
def print_help_msg(command):
    with click.Context(command) as ctx:
        click.echo(command.get_help(ctx))


# Use @cli.command() to create a sub command of cli.
@cli.command()
def help():
    """Show help messages."""
    # Print help message of cli.
    click.echo(print_help_msg(cli))

الآن يمكننا استخدام cli help في المحطة الطرفية:

$ python milvus_cli/scripts/milvus_cli.py help

إنشاء مجموعة فرعية لمجموعة أوامر

لا نريد فقط أن يكون لدينا أمر فرعي مثل cli help ، ولكننا نحتاج أيضًا إلى أوامر مجموعة فرعية مثل cli list collection و cli list partition و cli list indexes.

أولاً ننشئ أمر مجموعة فرعية list ، هنا يمكننا تمرير المعلمة الأولى إلى @cli.group كاسم الأمر بدلاً من استخدام اسم الدالة المحدد، حتى نتمكن من تقليل أسماء الدوال المكررة.

انتبه هنا، نستخدم @cli.group() بدلاً من @click.group حتى نتمكن من إنشاء مجموعة فرعية من المجموعة الأصلية.

نستخدم @click.pass_obj لتمرير context.obj إلى الأوامر الفرعية لهذه المجموعة الفرعية.

@cli.group('list', no_args_is_help=False)
@click.pass_obj
def listDetails(obj):
    """List collections, partitions and indexes."""
    pass

ثم نضيف بعض الأوامر الفرعية إلى هذه المجموعة الفرعية عن طريق @listDetails.command() (وليس @cli.command()). هذا مجرد مثال، يمكنك تجاهل التنفيذ وسنناقشه لاحقًا.

@listDetails.command()
@click.option('--timeout', 'timeout', help="[Optional] - An optional duration of time in seconds to allow for the RPC. When timeout is set to None, client waits until server response or error occur.", default=None)
@click.option('--show-loaded', 'showLoaded', help="[Optional] - Only show loaded collections.", default=False)
@click.pass_obj
def collections(obj, timeout, showLoaded):
    """List all collections."""
    try:
        obj.checkConnection()
        click.echo(obj.listCollections(timeout, showLoaded))
    except Exception as e:
        click.echo(message=e, err=True)


@listDetails.command()
@click.option('-c', '--collection', 'collection', help='The name of collection.', default='')
@click.pass_obj
def partitions(obj, collection):
    """List all partitions of the specified collection."""
    try:
        obj.checkConnection()
        validateParamsByCustomFunc(
            obj.getTargetCollection, 'Collection Name Error!', collection)
        click.echo(obj.listPartitions(collection))
    except Exception as e:
        click.echo(message=e, err=True)

بعد اكتمال كل هذه الأوامر، يكون لدينا أوامر مجموعة ميلتيغروب التي تبدو مثل:

image صورة

تخصيص أمر

إضافة خيارات

يمكنك إضافة بعض الخيارات إلى الأمر الذي سيتم استخدامه مثل cli --test-option value.

إليك مثال، نضيف ثلاثة خيارات alias و host و port لتحديد عنوان للاتصال بـ ميلتجروب.

تحدد المعاملتان الأوليان اسم الخيار القصير والكامل، وتحدد المعلمة الثالثة اسم المتغير، وتحدد المعلمة help رسالة المساعدة القصيرة، وتحدد المعلمة default القيمة الافتراضية وتحدد المعلمة type نوع القيمة.

وسيتم تمرير جميع قيم الخيارات إلى الدالة بترتيب التعريف.

@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
    pass

إضافة خيارات العلم

نحن نستخدم الخيارات أعلاه لتمرير قيمة، ولكن في بعض الأحيان نحتاج فقط إلى علامة كقيمة منطقية.

كما في المثال أدناه، الخيار autoId هو خيار علم ولا يمرر أي بيانات إلى الدالة، لذا يمكننا استخدامه مثل cli create collection -c c_name -p p_name -a.

@createDetails.command('collection')
@click.option('-c', '--collection-name', 'collectionName', help='Collection name to be created.', default='')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
    pass

إضافة الوسيطات

في هذا المشروع نستبدل كل استخدام الوسيطات باستخدام الخيارات. لكننا ما زلنا نقدم استخدام الوسيطات هنا. تختلف عن الخيارات، يتم استخدام الحجج مثل cli COMMAND [OPTIONS] ARGUEMENTS. إذا قمنا بتحويل المثال أعلاه إلى استخدام الحجج، فسيكون مثل هذا:

@createDetails.command('collection')
@click.argument('collectionName')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
    pass

ثم يجب أن يكون الاستخدام cli create collection c_name -p p_name -a.

إضافة رسالة تعليمات كاملة

كما حددنا رسالة المساعدة القصيرة أعلاه، يمكننا تحديد رسالة المساعدة الكاملة في الدالة:

@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
    """
    Connect to Milvus.

    Example:

        milvus_cli > connect -h 127.0.0.1 -p 19530 -a default
    """
    try:
        obj.connect(alias, host, port)
    except Exception as e:
        click.echo(message=e, err=True)
    else:
        click.echo("Connect Milvus successfully!")
        click.echo(obj.showConnection(alias))

الكتلة الأولى داخل الدالة هي رسالة المساعدة التي ستتم طباعتها بعد أن ندخل cli connect --help.

milvus_cli > connect --help
Usage: milvus_cli.py connect [OPTIONS]

  Connect to Milvus.

  Example:

      milvus_cli > connect -h 127.0.0.1 -p 19530 -a default

Options:
  -a, --alias TEXT    Milvus link alias name, default is `default`.
  -h, --host TEXT     Host name, default is `127.0.0.1`.
  -p, --port INTEGER  Port, default is `19530`.
  --help              Show this message and exit.

إضافة تأكيد

نحتاج أحيانًا إلى أن يقوم المستخدم بتأكيد بعض الإجراءات وخاصة حذف شيء ما. يمكننا إضافة click.confirm لإيقاف المستخدم مؤقتًا ونطلب منه التأكيد:

@deleteSth.command('collection')
@click.option('-c', '--collection', 'collectionName', help='The name of collection to be deleted.', default='')
@click.option('-t', '--timeout', 'timeout', help='An optional duration of time in seconds to allow for the RPC. If timeout is set to None, the client keeps waiting until the server responds or an error occurs.', default=None, type=int)
@click.pass_obj
def deleteCollection(obj, collectionName, timeout):
    """
    Drops the collection together with its index files.

    Example:

        milvus_cli > delete collection -c car
    """
    click.echo(
        "Warning!\nYou are trying to delete the collection with data. This action cannot be undone!\n")
    if not click.confirm('Do you want to continue?'):
        return
    pass

كما في المثال أعلاه، ستظهر محادثة تأكيد مثل Aborted!ant to continue? [y/N]:.

إضافة مطالبات

لتنفيذ المطالبات نحتاج فقط إلى إضافة click.prompt.

@cli.command()
@click.pass_obj
def query(obj):
    """
    Query with a set of criteria, and results in a list of records that match the query exactly.
    """
    collectionName = click.prompt(
        'Collection name', type=click.Choice(obj._list_collection_names()))
    expr = click.prompt('The query expression(field_name in [x,y])')
    partitionNames = click.prompt(
        f'The names of partitions to search(split by "," if multiple) {obj._list_partition_names(collectionName)}', default='')
    outputFields = click.prompt(
        f'Fields to return(split by "," if multiple) {obj._list_field_names(collectionName)}', default='')
    timeout = click.prompt('timeout', default='')
    pass

ستظهر المطالبة عند كل click.prompt. نستخدم بعض المطالبات في سلسلة بحيث تبدو وكأنها محادثة مستمرة. هذا يضمن أن المستخدم سيدخل البيانات بالترتيب الذي نريده. في هذه الحالة نحتاج إلى أن يختار المستخدم مجموعة أولاً، ونحتاج إلى الحصول على جميع الأقسام ضمن هذه المجموعات، ثم نعرضها للمستخدم للاختيار.

إضافة اختيارات

في بعض الأحيان تريد أن يقوم المستخدم بإدخال نطاق/نوع محدود من القيمة، يمكنك إضافة type=click.Choice([<any>]) إلى click.prompt و click.options وغيرها...

مثل,

collectionName = click.prompt(
        'Collection name', type=click.Choice(['collection_1', 'collection_2']))

ثم يمكن للمستخدم فقط إدخال collection_1 أو collection_2 ، سيتم رفع الخطأ في حالة وجود أي مدخلات أخرى.

إضافة شاشة واضحة

يمكنك استخدام click.clear() لتنفيذ ذلك.

@cli.command()
def clear():
    """Clear screen."""
    click.clear()

نصائح إضافية

  • القيمة الافتراضية هي None ، لذلك لا معنى لها إذا قمت بتحديد القيمة الافتراضية على أنها None. وستؤدي القيمة الافتراضية None إلى إظهار click.prompt باستمرار إذا كنت تريد ترك قيمة فارغة لتجاوزها.

تنفيذ مطالبة CLI للمستخدم بإدخال CLI

لماذا مطالبة CLI

لتشغيل قاعدة البيانات، نحتاج إلى اتصال مستمر بمثيل. إذا استخدمنا وضع سطر الأوامر الأصلي، سيتم قطع الاتصال بعد كل أمر يتم تنفيذه. نريد أيضًا تخزين بعض البيانات عند استخدام CLI، وتنظيفها بعد الخروج.

تنفيذ

  1. استخدم while True للاستماع المستمر لمدخلات المستخدم.
def runCliPrompt():
    while True:
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue


if __name__ == '__main__':
    runCliPrompt()
  1. سيؤدي استخدام input فقط إلى تحويل up و down و left و right مفاتيح الأسهم و tab وبعض المفاتيح الأخرى إلى سلسلة Acsii تلقائيًا. إلى جانب ذلك لا يمكن قراءة أوامر السجل من الجلسة. لذلك نضيف readline إلى الدالة runCliPrompt.
def runCliPrompt():
    while True:
            import readline
        readline.set_completer_delims(' \t\n;')
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue
  1. إضافة quit CLI.
@cli.command('exit')
def quitapp():
    """Exit the CLI."""
    global quitapp
    quitapp = True


quitapp = False  # global flag


def runCliPrompt():
    while not quitapp:
            import readline
        readline.set_completer_delims(' \t\n;')
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue
  1. قبض على خطأ KeyboardInterrupt عند استخدام ctrl C للخروج.
def runCliPrompt():
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)
  1. بعد تسوية كل شيء، تبدو CLI الآن مثل:
milvus_cli >
milvus_cli > connect
+-------+-----------+
| Host  | 127.0.0.1 |
| Port  |   19530   |
| Alias |  default  |
+-------+-----------+

milvus_cli > help
Usage:  [OPTIONS] COMMAND [ARGS]...

  Milvus CLI

Commands:
  clear     Clear screen.
  connect   Connect to Milvus.
  create    Create collection, partition and index.
  delete    Delete specified collection, partition and index.
  describe  Describe collection or partition.
  exit      Exit the CLI.
  help      Show help messages.
  import    Import data from csv file with headers and insert into target...
  list      List collections, partitions and indexes.
  load      Load specified collection.
  query     Query with a set of criteria, and results in a list of...
  release   Release specified collection.
  search    Conducts a vector similarity search with an optional boolean...
  show      Show connection, loading_progress and index_progress.
  version   Get Milvus CLI version.

milvus_cli > exit

تنفيذ الإكمال التلقائي يدويًا

يختلف مشروعنا عن الإكمال التلقائي لصدفة النقر، حيث يقوم مشروعنا بلف سطر الأوامر واستخدام حلقة للحصول على مدخلات المستخدم لتنفيذ سطر الأوامر الفوري. لذلك نحن بحاجة إلى ربط الإكمال التلقائي بـ readline.

class Completer(object):
    RE_SPACE = re.compile('.*\s+$', re.M)
    CMDS_DICT = {
        'clear': [],
        'connect': [],
        'create': ['collection', 'partition', 'index'],
        'delete': ['collection', 'partition', 'index'],
        'describe': ['collection', 'partition'],
        'exit': [],
        'help': [],
        'import': [],
        'list': ['collections', 'partitions', 'indexes'],
        'load': [],
        'query': [],
        'release': [],
        'search': [],
        'show': ['connection', 'index_progress', 'loading_progress'],
        'version': [],
    }

    def __init__(self) -> None:
        super().__init__()
        self.COMMANDS = list(self.CMDS_DICT.keys())
        self.createCompleteFuncs(self.CMDS_DICT)

    def createCompleteFuncs(self, cmdDict):
        for cmd in cmdDict:
            sub_cmds = cmdDict[cmd]
            complete_example = self.makeComplete(cmd, sub_cmds)
            setattr(self, 'complete_%s' % cmd, complete_example)

    def makeComplete(self, cmd, sub_cmds):
        def f_complete(args):
            f"Completions for the {cmd} command."
            if not args:
                return self._complete_path('.')
            if len(args) <= 1 and not cmd == 'import':
                return self._complete_2nd_level(sub_cmds, args[-1])
            return self._complete_path(args[-1])
        return f_complete

    def _listdir(self, root):
        "List directory 'root' appending the path separator to subdirs."
        res = []
        for name in os.listdir(root):
            path = os.path.join(root, name)
            if os.path.isdir(path):
                name += os.sep
            res.append(name)
        return res

    def _complete_path(self, path=None):
        "Perform completion of filesystem path."
        if not path:
            return self._listdir('.')
        dirname, rest = os.path.split(path)
        tmp = dirname if dirname else '.'
        res = [os.path.join(dirname, p)
               for p in self._listdir(tmp) if p.startswith(rest)]
        # more than one match, or single match which does not exist (typo)
        if len(res) > 1 or not os.path.exists(path):
            return res
        # resolved to a single directory, so return list of files below it
        if os.path.isdir(path):
            return [os.path.join(path, p) for p in self._listdir(path)]
        # exact file match terminates this completion
        return [path + ' ']

    def _complete_2nd_level(self, SUB_COMMANDS=[], cmd=None):
        if not cmd:
            return [c + ' ' for c in SUB_COMMANDS]
        res = [c for c in SUB_COMMANDS if c.startswith(cmd)]
        if len(res) > 1 or not (cmd in SUB_COMMANDS):
            return res
        return [cmd + ' ']

    def complete(self, text, state):
        "Generic readline completion entry point."
        buffer = readline.get_line_buffer()
        line = readline.get_line_buffer().split()
        # show all commands
        if not line:
            return [c + ' ' for c in self.COMMANDS][state]
        # account for last argument ending in a space
        if self.RE_SPACE.match(buffer):
            line.append('')
        # resolve command to the implementation function
        cmd = line[0].strip()
        if cmd in self.COMMANDS:
            impl = getattr(self, 'complete_%s' % cmd)
            args = line[1:]
            if args:
                return (impl(args) + [None])[state]
            return [cmd + ' '][state]
        results = [
            c + ' ' for c in self.COMMANDS if c.startswith(cmd)] + [None]
        return results[state]

بعد تعريف Completer يمكننا ربطه بـ readline:

comp = Completer()


def runCliPrompt():
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)

إضافة خيار لمرة واحدة

بالنسبة لسطر الأوامر الموجهة، في بعض الأحيان لا نريد أن ندخل بشكل كامل إلى البرامج النصية للحصول على بعض المعلومات مثل الإصدار. مثال جيد على ذلك هو Python ، عندما تكتب python في المحطة الطرفية سيظهر سطر الأوامر Promtp، لكنه يُرجع رسالة إصدار فقط ولن يدخل إلى البرامج النصية السريعة إذا كتبت python -V. لذلك يمكننا استخدام sys.args في التعليمات البرمجية الخاصة بنا للتنفيذ.

def runCliPrompt():
    args = sys.argv
    if args and (args[-1] == '--version'):
        print(f"Milvus Cli v{getPackageVersion()}")
        return
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)


if __name__ == '__main__':
    runCliPrompt()

نحصل على sys.args قبل الحلقة عند التشغيل لأول مرة في البرامج النصية لـ CLI. إذا كانت الوسيطة الأخيرة هي --version ، فإن الكود سيعيد إصدار الحزمة دون الدخول في حلقة.

سيكون ذلك مفيدًا بعد أن نبني الأكواد كحزمة. يمكن للمستخدم أن يكتب milvus_cli للانتقال إلى CLI موجهة، أو اكتب milvus_cli --version للحصول على الإصدار فقط.

البناء والإصدار

أخيرًا نريد بناء حزمة وإصدارها بواسطة PYPI. بحيث يمكن للمستخدم ببساطة استخدام pip install <package name> للتثبيت.

تثبيت محلي للاختبار

قبل أن تقوم بنشر الحزمة على PYPI، قد ترغب في تثبيتها محليًا لبعض الاختبارات.

في هذه الحالة، يمكنك ببساطة cd في دليل الحزمة وتشغيل pip install -e . (لا تنسى .).

إنشاء ملفات الحزمة

ارجع إلى: https://packaging.python.org/tutorials/packaging-projects/

يجب أن تبدو بنية الحزمة كما يلي:

package_example/
├── LICENSE
├── README.md
├── setup.py
├── src/
│   ├── __init__.py
│   ├── main.py
│   └── scripts/
│       ├── __init__.py
│       └── example.py
└── tests/

إنشاء دليل الحزمة

قم بإنشاء دليل Milvus_cli بالهيكل أدناه:

Milvus_cli/
├── LICENSE
├── README.md
├── setup.py
├── milvus_cli/
│   ├── __init__.py
│   ├── main.py
│   ├── utils.py
│   └── scripts/
│       ├── __init__.py
│       └── milvus_cli.py
└── dist/

اكتب رمز الإدخال

يجب أن يكون إدخال النص البرمجي في Milvus_cli/milvus_cli/scripts ، ويجب أن يكون Milvus_cli/milvus_cli/scripts/milvus_cli.py مثل:

import sys
import os
import click
from utils import PyOrm, Completer


pass_context = click.make_pass_decorator(PyOrm, ensure=True)


@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
    """Milvus CLI"""
    ctx.obj = PyOrm()

"""
...
Here your code.
...
"""

@cli.command('exit')
def quitapp():
    """Exit the CLI."""
    global quitapp
    quitapp = True


quitapp = False  # global flag
comp = Completer()


def runCliPrompt():
    args = sys.argv
    if args and (args[-1] == '--version'):
        print(f"Milvus Cli v{getPackageVersion()}")
        return
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
            except Exception as e:
                click.echo(
                    message=f"Error occurred!\n{str(e)}", err=True)
    except KeyboardInterrupt:
        sys.exit(0)


if __name__ == '__main__':
    runCliPrompt()

تحرير setup.py

from setuptools import setup, find_packages

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name='milvus_cli',
    version='0.1.6',
    author='Milvus Team',
    author_email='milvus-team@zilliz.com',
    url='https://github.com/milvus-io/milvus_cli',
    description='CLI for Milvus',
    long_description=long_description,
    long_description_content_type='text/markdown',
    license='Apache-2.0',
    packages=find_packages(),
    include_package_data=True,
    install_requires=[
        'Click==8.0.1',
        'pymilvus==2.0.0rc5',
        'tabulate==0.8.9'
    ],
    entry_points={
        'console_scripts': [
            'milvus_cli = milvus_cli.scripts.milvus_cli:runCliPrompt',
        ],
    },
    python_requires='>=3.8'
)

بعض النصائح هنا:

  1. نستخدم محتوى README.md كوصف طويل للحزمة.
  2. أضف جميع التبعيات إلى install_requires.
  3. حدد ال entry_points. في هذه الحالة، نقوم بتعيين milvus_cli كتابع لـ console_scripts ، بحيث يمكننا كتابة milvus_cli كأمر مباشرة بعد تثبيت هذه الحزمة. ونقطة الدخول milvus_cli هي الدالة runCliPrompt في milvus_cli/scripts/milvus_cli.py.

بناء

  1. قم بترقية الحزمة build: python3 -m pip install --upgrade build

  2. قم بتشغيل الإنشاء: python -m build --sdist --wheel --outdir dist/ .

  3. سيتم إنشاء ملفين ضمن الدليل dist/:

dist/
  example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
  example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz

نشر الإصدار

الرجوع إلى: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives

  1. ترقية الحزمة twine: python3 -m pip install --upgrade twine
  2. قم بالتحميل إلى PYPI اختبار البيئة : python3 -m twine upload --repository testpypi dist/*
  3. تحميل إلى PYPI: python3 -m twine upload dist/*

CI / CD بواسطة سير عمل Github

الرجوع إلى: https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

نريد طريقة لتحميل الأصول تلقائيًا، ويمكنه بناء الحزم وتحميلها إلى إصدارات github و PYPI.

(لسبب ما نريد فقط سير العمل فقط نشر الإصدار لاختبار PYPI).

# This is a basic workflow to help you get started with Actions

name: Update the release's assets after it published

# Controls when the workflow will run
on:
  release:
    # The workflow will run after release published
    types: [published]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: '3.8'
          architecture: 'x64'
      - name: Install pypa/build
        run: >-
          python -m
          pip install
          build
          --user
      - name: Clean dist/
        run: |
          sudo rm -fr dist/*
      - name: Build a binary wheel and a source tarball
        run: >-
          python -m
          build
          --sdist
          --wheel
          --outdir dist/
          .
      # Update target github release's assets
      - name: Update assets
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: ./dist/*
      - name: Publish distribution 📦 to Test PyPI
        if: contains(github.ref, 'beta') && startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.TEST_PYPI_API_TOKEN }}
          repository_url: https://test.pypi.org/legacy/
          packages_dir: dist/
          verify_metadata: false

تعرف على المزيد حول ميلفوس

Milvus أداة قوية قادرة على تشغيل مجموعة واسعة من تطبيقات الذكاء الاصطناعي والبحث عن التشابه المتجه. لمعرفة المزيد عن المشروع، اطلع على الموارد التالية:

  • اقرأ مدونتنا.
  • تفاعل مع مجتمعنا مفتوح المصدر على Slack.
  • استخدم أو ساهم في قاعدة بيانات المتجهات الأكثر شعبية في العالم على GitHub.
  • اختبار تطبيقات الذكاء الاصطناعي ونشرها بسرعة من خلال معسكرنا التدريبي الجديد.

Like the article? Spread the word

استمر في القراءة