Skip to content

Commit

Permalink
Merge pull request #26 from Azure/main
Browse files Browse the repository at this point in the history
pull for azure/main
  • Loading branch information
RAY-316 authored Nov 25, 2021
2 parents 3cd0982 + 9e1d561 commit 960d12c
Show file tree
Hide file tree
Showing 485 changed files with 96,426 additions and 8,399 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/sdk/eventhub/ @annatisch @yunhaoling @swathipil @rakshith91

# PRLabel: %Storage
/sdk/storage/ @amishra-dev @zezha-msft @annatisch @xiafu-msft @tasherif-msft @kasobol-msft
/sdk/storage/ @amishra-dev @zezha-msft @annatisch @tasherif-msft

# PRLabel: %App Configuration
/sdk/appconfiguration/ @xiangyan99 @YalinLi0312
Expand Down
19 changes: 0 additions & 19 deletions doc/dev/get_unreleased_package_guide.md

This file was deleted.

88 changes: 62 additions & 26 deletions doc/dev/packaging.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
# Azure packaging

This article describes how to declare setup.py and all packaging information for packages inside the `azure` namespace
This article describes the recommendations for defining namespace packaging to release a package inside the `azure` namespace. Being inside the `azure` namespace means that a service `myservice` can be imported using:
```python
import azure.myservice
```

Namespace packaging is complicated in Python, here's a few reading if you still doubt it:
- https://packaging.python.org/guides/packaging-namespace-packages/
- https://www.python.org/dev/peps/pep-0420/
- https://github.com/pypa/sample-namespace-packages

This article describes the recommendation on how to define namespace packaging to release a package inside the `azure` namespace. Being inside the `azure` namespace meaning you have a service `myservice` that you want to import using:
```python
import azure.myservice
```

Note:
- This article is not about setup.py or setup.cfg or the right way to *write* the packaging, it's about what instructions you should use to achieve this. If you are fluent in setuptools, and prefer to write the suggestions in setup.cfg and not in setup.py, this is not a concern.
While this article provides an example using setup.py, this can also be achieved with setup.cfg or other methods, as long as the constraints on the final wheels/sdist are met.

*This page has been updated to be Python 3 only packages as we do not recommend supporting Python 2 after January 1st 2022.* If you still want to support Python 2 for some reasons, there is a section at the bottom with some details (or you have the Github history, to read the page as it was on November 1st 2021).

# What are the constraints?

We want to build sdist and wheels in order to follow the following constraints:
- Solution should work with *recent* versions of pip and setuptools (not the very latest only, but not archaeology either)
- Wheels must work with Python 2.7 and 3.6+
- easy-install scenario is a plus, but cannot be considered critical anymore
- Wheels must work with Python 3.7+
- mixed dev installation and PyPI installation should be explicitly addressed

# What do I do in my files to achieve that
Expand Down Expand Up @@ -55,13 +54,9 @@ The "packages" section MUST EXCLUDE the `azure` package. Example:
]),
```

The "extras_requires" section MUST include a conditional dependency on "azure-nspkg" for Python 2. There is also a conditional dependency on "typing" for Python 3.5 because of the type-hinting for Python 3.5 and above. Example:

Since the package is Python 3 only, you must notify it in the setup.py as well:
```python
extras_require={
":python_version<'3.0'": ['azure-nspkg'],
":python_version<'3.5'": ['typing'],
}
python_requires=">=3.7",
```

Example of a full setup.py
Expand Down Expand Up @@ -113,16 +108,15 @@ setup(
classifiers=[
'Development Status :: 4 - Beta',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'License :: OSI Approved :: MIT License',
],
python_requires=">=3.7",
zip_safe=False,
packages=find_packages(exclude=[
'tests',
Expand All @@ -134,17 +128,59 @@ setup(
'msrestazure>=0.4.32,<2.0.0',
'azure-common~=1.1',
],
extras_require={
":python_version<'3.0'": ['azure-nspkg'],
":python_version<'3.5'": ['typing'],
}
)
```

This syntax works with setuptools >= 17.1 and pip >= 6.0, which is considered enough to support in 2019.
This syntax works with setuptools >= 24.2.0 (July 2016) and pip >= 9.0 (Nov 2016), which is considered enough to support in 2021.

Since the package is Python 3 only, do NOT make this wheel universal. This usually means you should NOT have `universal=1` in the `setup.cfg`. It may mean you can completely remove the file if `universal` was the only configuration option inside.

# How can I check if my packages are built correctly?

- wheels must NOT contain a `azure/__init__.py` file (you can open it with a zip util to check)
- wheels installs `azure-nskpg` ONLY on Python 2.
- wheel file must NOT contain a `azure/__init__.py` file (you can open it with a zip util to check)
- wheel file name suffix is `py3-none-any`, and NOT `py2.py3-none-any`.
- sdist must contain a `azure/__init__.py` file that declares `azure` as a namespace package using the `pkgutil` syntax

# I already have a package that supports Python 2, can I get short version on how to udpate to Python 3 only?

- Remove "universal" from setup.cfg, or completly remove the file if it was the only option
- In setup.py:
- Remove `extra_requires`
- Add `python_requires=">=3.7",`
- Remove the Python 2 and 3.5/3.6 classifiers
- Add classifier `Programming Language :: Python :: 3 :: Only`
- Remove the "azure" check if applicable (see next note)

# Note on checking old Azure packages

You may see code in `setup.py` looking like this:
```python
# azure v0.x is not compatible with this package
# azure v0.x used to have a __version__ attribute (newer versions don't)
try:
import azure

try:
VER = azure.__version__ # type: ignore
raise Exception(
"This package is incompatible with azure=={}. ".format(VER) + 'Uninstall it with "pip uninstall azure".'
)
except AttributeError:
pass
except ImportError:
pass
```

This was to prevent some difficult update scenario 6 years ago, and can be safely removed from your setup.py

# Note on Python 2

The "extras_requires" section MUST include a conditional dependency on "azure-nspkg" for Python 2. Example:

```python
extras_require={
":python_version<'3.0'": ['azure-nspkg'],
}
```

An additional verification is that wheels installs `azure-nskpg` ONLY on Python 2.
104 changes: 92 additions & 12 deletions doc/dev/perfstress_tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
- [The PerfStressTest base](#the-perfstresstest-base)
- [Default command options](#default-command-options)
- [Running with test proxy](#running-with-the-test-proxy)
- [The BatchPerfTest base](#the-batchperftest-base)
2. [Adding performance tests to an SDK](#adding-performance-tests-to-an-sdk)
- [Writing a test](#writing-a-test)
- [Writing a batch test](#writing-a-batch-test)
- [Adding legacy T1 tests](#adding-legacy-t1-tests)
3. [Running the tests](#running-the-tests)
- [Running the system tests](#running-the-system-tests)
4. [Readme](#readme)

# The perfstress framework
Expand All @@ -20,6 +23,7 @@ the tests. To start using the framework, make sure that `azure-devtools` is incl
The perfstress framework offers the following:
- The `perfstress` commandline tool.
- The `PerfStressTest` baseclass.
- The `BatchPerfTest` baseclass.
- Stream utilities for uploading/downloading without storing in memory: `RandomStream`, `AsyncRandomStream`, `WriteStream`.
- A `get_random_bytes` utility for returning randomly generated data.
- A series of "system tests" to test the perfstress framework along with the performance of the raw transport layers (requests, aiohttp, etc).
Expand All @@ -39,14 +43,14 @@ class PerfStressTest:
async def global_cleanup(self):
# Can be optionally defined. Only run once, regardless of parallelism.

async def record_and_start_playback(self):
# Set up the recording on the test proxy, and configure the proxy in playback mode.
# This function is only run if a test proxy URL is provided (-x).
async def post_setup(self):
# Post-setup called once per parallel test instance.
# Used by base classes to setup state (like test-proxy) after all derived class setup is complete.
# There should be no need to overwrite this function.

async def stop_playback(self):
# Configure the proxy out of playback mode and discard the recording.
# This function is only run if a test proxy URL is provided (-x).
async def pre_cleanup(self):
# Pre-cleanup called once per parallel test instance.
# Used by base classes to cleanup state (like test-proxy) before all derived class cleanup runs.
# There should be no need to overwrite this function.

async def setup(self):
Expand Down Expand Up @@ -82,9 +86,9 @@ The framework has a series of common command line options built in:
- `-w --warm-up=5` Number of seconds to spend warming up the connection before measuring begins. Default is 5.
- `--sync` Whether to run the tests in sync or async. Default is False (async).
- `--no-cleanup` Whether to keep newly created resources after test run. Default is False (resources will be deleted).
- `-x --test-proxies` Whether to run the tests against the test proxy server. Specify the URL(s) for the proxy endpoint(s) (e.g. "https://localhost:5001").
- `--profile` Whether to run the perftest with cProfile. If enabled (default is False), the output file of the **last completed single iteration** will be written to the current working directory in the format `"cProfile-<TestClassName>-<TestID>-<sync/async>.pstats"`.

- `--insecure` Whether to run without SSL validation. Default is False.
- `-x --test-proxies` Whether to run the tests against the test proxy server. Specify the URL(s) for the proxy endpoint(s) (e.g. "https://localhost:5001"). Multiple values should be semi-colon-separated.
- `--profile` Whether to run the perftest with cProfile. If enabled (default is False), the output file of a single iteration will be written to the current working directory in the format `"cProfile-<TestClassName>-<TestID>-<sync|async>.pstats"`.

## Running with the test proxy
Follow the instructions here to install and run the test proxy server:
Expand All @@ -94,6 +98,29 @@ Once running, in a separate process run the perf test in question, combined with
```cmd
(env) ~/azure-storage-blob/tests> perfstress DownloadTest -x "https://localhost:5001"
```
## The BatchPerfTest base
The `BatchPerfTest` class is the parent class of the above `PerfStressTest` class that is further abstracted to allow for more flexible testing of SDKs that don't conform to a 1:1 ratio of operations to results.
An example of this is a messaging SDK that streams multiple messages for a period of time.
This base class uses the same setup/cleanup/close functions described above, however instead of `run_sync` and `run_async`, it has `run_batch_sync` and `run_batch_async`:
```python
class BatchPerfTest:

def run_batch_sync(self) -> int:
"""
Run cumultive operation(s) - i.e. an operation that results in more than a single logical result.
:returns: The number of completed results.
:rtype: int
"""

async def run_batch_async(self) -> int:
"""
Run cumultive operation(s) - i.e. an operation that results in more than a single logical result.
:returns: The number of completed results.
:rtype: int
"""

```
An example test case using the `BatchPerfTest` base can be found below.

# Adding performance tests to an SDK
The performance tests will be in a submodule called `perfstress_tests` within the `tests` directory in an SDK project.
Expand Down Expand Up @@ -314,6 +341,44 @@ class DownloadTest(_StorageStreamTestBase):
stream = await self.async_blob_client.download_blob(max_concurrency=self.args.max_concurrency)
await stream.readinto(self.download_stream)
```
## Writing a batch test
#### Example messaging receive test
```python
from azure_devtools.perfstress_tests import BatchPerfTest

from azure.messaging.foo import MockReceiver
from azure.messaging.foo.aio import MockReceiver as AsyncMockReceiver

class MessageReceiveTest(BatchPerfTest):
def __init__(self, arguments):
super().__init__(arguments)

# Setup service clients
self.receiver_client = MockReceiver()
self.async_receiver_client = AsyncMockReceiver()

def run_batch_sync(self) -> int:
messages = self.receiver_client.receive(
max_messages=self.args.max_message_count,
min_messages=self.args.min_message_count
)
return len(messages)

async def run_batch_async(self) -> int:
messages = await self.async_receiver_client.receive(
max_messages=self.args.max_message_count,
min_messages=self.args.min_message_count
)
return len(messages)

@staticmethod
def add_arguments(parser):
super(MessageReceiveTest, MessageReceiveTest).add_arguments(parser)
parser.add_argument('--max-message-count', nargs='?', type=int, default=10)
parser.add_argument('--min-message-count', nargs='?', type=int, default=0)

```

## Adding legacy T1 tests
To compare performance against T1 libraries, you can add tests for a legacy SDK. To do this, add a submodule into the `perfstress_tests` module called `T1_legacy_tests` (and add an empty `__init__.py`).
To configure the exact T1 SDK you wish to compare perf against, add a `t1_test_requirements.txt` file to install any package requirements. Note that this will likely be incompatible with the T2 SDK testing environment, and running the legacy tests will probably need to be from a separate virtual environment (see the [Running the tests](#running-the-tests) section below).
Expand Down Expand Up @@ -360,15 +425,30 @@ AZURE_STORAGE_CONNECTION_STRING=<live storage account connection string>
When `azure-devtools` is installed, you will have access to the `perfstress` command line tool, which will scan the current module for runable perf tests. Only a specific test can be run at a time (i.e. there is no "run all" feature).

```cmd
(env) ~/azure-storage-file-share> cd tests
(env) ~/azure-storage-file-share/tests> perfstress
(env) ~/azure-storage-file-share> perfstress
```
Using the `perfstress` command alone will list the available perf tests found. Note that the available tests discovered will vary depending on whether your environment is configured for the T1 or T2 SDK.
If your tests are not being discovered, run the `perfstressdebug` command instead for additional logging.

### Example test run command
```cmd
(env) ~/azure-storage-file-share/tests> perfstress UploadTest --parallel=2 --size=10240
(env) ~/azure-storage-file-share> perfstress UploadTest --parallel=2 --size=10240
```
## Running the system tests
The system tests are used to test the performance of the Python HTTP layers exclusive of the Azure SDK in order to set a performance benchmark.
In order to run these, you will need a Python environment with `systemperf` flavour of `azure-devtools` installed. Installing to a fresh Python environment is recommended.
```cmd
(env) ~/> pip install -e azure-sdk-for-python/tools/azure-devtools[systemperf]
```
Once these dependencies are installed, the `systemperf` command can be run directly to list the available tests:
```cmd
(env)~/> systemperf
```
A specific test can be run in the same manner as an SDK perf test:
```cmd
(env)~/> systemperf AioHttpGetTest --url="http://test-endpoint.com"
```


# Readme

Expand Down
Binary file added doc/dev/private_package/default_tag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/dev/private_package/different_folders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions doc/dev/private_package/get_private_package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Get Private Package

This guide is to help Python SDK users to get private package. Just follow the steps:

## 1. Confirm default tag

Make sure your target tag is defined in `readme.md` and **default tag is same with your target tag**. For example:

[azure-rest-api-specs/specification/network/resource-manager at main · Azure/azure-rest-api-specs (github.com)](https://github.com/Azure/azure-rest-api-specs/tree/main/specification/network/resource-manager#basic-information)

![](default_tag.png)

## 2.Configure `readme.python.md`

If there is no `Python multi-api` in `readme.python.md`(i.e. [datadog](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/datadog/resource-manager/readme.python.md)), skip this step.

If there is `Python multi-api` in `readme.python.md` (i.e. [network](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/network/resource-manager/readme.python.md#python-multi-api)), you need additional configuration: [Python Multiapi Configuration](python_multiapi_configuration.md)

## 3.Trigger pipeline

Submit a PR or draft PR to [Azure/azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs)

![](unreleased_package_guide_example1.png)

## 4.Get private package

Wait until pipelines finish, then there will be wheel and zip of the package. Just Click to download them.

![](unreleased_package_guide_example2.png)

If there is no link in the figure above, it may be folded. You can also find it in the `Checks`.

![img.png](unreleased_package_guide_example3.png)

# Note

## 1.private repo

In private repo [Azure/azure-rest-api-specs-pr](https://github.com/Azure/azure-rest-api-specs-pr), pipeline can be triggered **only when the target branch is `main`**

Binary file added doc/dev/private_package/one_folder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/dev/private_package/python_config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 960d12c

Please sign in to comment.